gpt4 book ai didi

c++ - Haskell 中的面向对象编程

转载 作者:可可西里 更新时间:2023-11-01 16:24:13 26 4
gpt4 key购买 nike

我试图了解 Haskell 中的面向对象风格编程,因为我知道由于缺乏可变性,事情会有所不同。我玩过类型类,但我对它们的理解仅限于它们作为接口(interface)。所以我编写了一个 C++ 示例,它是具有纯基和虚拟继承的标准菱形。 Bat继承 FlyingMammal , 和 FlyingMammal继承 Animal .

#include <iostream>

class Animal
{
public:
virtual std::string transport() const = 0;
virtual std::string type() const = 0;
std::string describe() const;
};

std::string Animal::describe() const
{ return "I am a " + this->transport() + " " + this->type(); }

class Flying : virtual public Animal
{
public:
virtual std::string transport() const;
};

std::string Flying::transport() const { return "Flying"; }

class Mammal : virtual public Animal
{
public:
virtual std::string type() const;
};

std::string Mammal::type() const { return "Mammal"; }

class Bat : public Flying, public Mammal {};

int main() {
Bat b;
std::cout << b.describe() << std::endl;
return 0;
}

基本上我对如何将这样的结构翻译成 Haskell 感兴趣,基本上这将允许我拥有 Animal 的列表s,就像我可以有一个指向 Animal 的(智能)指针数组在 C++ 中。

最佳答案

你只是不想这样做,甚至不要开始。 OO 确实有其优点,但是像 C++ 这样的“经典示例”几乎总是人为设计的结构,旨在将范式锤入本科生的大脑,这样他们就不会开始提示他们应该使用的语言有多愚蠢† .

这个想法似乎基本上是通过您的编程语言中的对象来建模“现实世界的对象”。对于实际的编程问题,这可能是一种很好的方法,但只有当您实际上可以在如何使用真实世界的对象和如何在程序中处理 OO 对象之间进行类比时,它才有意义。

对于这样的动物例子来说,这简直是 absurd 的。如果有的话,这些方法必须是“饲料”、“牛奶”、“屠宰”之类的东西……但“运输”是用词不当,我认为它实际上是移动动物,这宁愿是一种方法动物生活的环境,基本上只有作为游客模式的一部分才有意义。
describe , type以及您所说的 transport另一方面,要简单得多。这些基本上是依赖于类型的常量或简单的纯函数。只有 OO 偏执狂才批准将它们设为类方法。

如果您不尝试将其强制转换为类似 OO 的东西,而只保留 Haskell 中的(有用类型的)数据,那么任何与这种动物 Material 类似的东西(基本上只有数据)都会变得更简单。

所以这个例子显然没有给我们带来任何进一步的结果,让我们考虑一下 OOP 确实有意义的事情。小部件工具包浮现在脑海中。就像是

class Widget;

class Container : public Widget {
std::vector<std::unique_ptr<Widget>> children;
public:
// getters ...
};
class Paned : public Container { public:
Rectangle childBoundaries(int) const;
};
class ReEquipable : public Container { public:
void pushNewChild(std::unique_ptr<Widget>&&);
void popChild(int);
};
class HJuxtaposition: public Paned, public ReEquipable { ... };

为什么 OO 在这里有意义?首先,它很容易让我们存储异构的小部件集合。这实际上在 Haskell 中并不容易实现,但在尝试之前,您可能会问自己是否真的需要它。毕竟,对于某些容器,允许这样做可能不是那么可取。在 Haskell 中,参数多态性非常好用。对于任何给定类型的小部件,我们观察 Container 的功能。几乎可以简化为一个简单的列表。那么为什么不直接使用列表,无论您需要 Container ?

当然,在这个例子中,你可能会发现你确实需要异构容器;最直接的获取方式是 {-# LANGUAGE ExistentialQuantification #-} :

data GenericWidget = GenericWidget { forall w . Widget w => getGenericWidget :: w }

在这种情况下 Widget将是一个类型类(可能是抽象类 Widget 的相当直译)。在 Haskell 中,这是不得已而为之的事情,但可能就在这里。
Paned更多的是一个界面。我们可能在这里使用另一个类型类,基本上是对 C++ 的音译:

class Paned c where
childBoundaries :: c -> Int -> Maybe Rectangle
ReEquipable更困难,因为它的方法实际上改变了容器。这在 Haskell 中显然是有问题的。但是您可能会再次发现没有必要:如果您已替换 Container按普通列表分类,您可以将更新作为纯功能更新进行。

不过,可能这对于手头的任务来说效率太低了。充分讨论有效地进行可变更新的方法对于这个答案的范围来说太过分了,但是这种方法是存在的,例如使用 lenses .

概括

OO 不能很好地转换为 Haskell。没有一个简单的通用同构,只有多个近似值可供选择,需要经验。您应该尽可能避免从 OO 的角度来处理问题,而是考虑数据、函数、monad 层。事实证明,这会让你在 Haskell 中走得很远。只有在少数应用程序中,OO 是如此自然,值得将其压入语言中。

†对不起,这个话题总是让我进入强烈的观点咆哮模式......

‡这些偏执的部分原因是可变性的麻烦,而这在 Haskell 中不会出现。

关于c++ - Haskell 中的面向对象编程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20184286/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com