c – Haskell中面向对象的编程
作者:互联网
我试图了解Haskell中面向对象的样式编程,知道由于缺乏可变性,事情会有所不同.我玩过类型类,但我对它们的理解仅限于它们作为接口.所以我编写了一个C示例,它是具有纯基础和虚拟继承的标准菱形.蝙蝠继承飞行和哺乳动物,飞行和哺乳动物都继承了动物.
#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感兴趣,基本上这将允许我有一个动物列表,就像我可以在C中拥有一系列(智能)动物指针.
解决方法:
你只是不想那样做,甚至不要开始. OO肯定有它的优点,但像你的C之类的“经典例子”几乎总是设计用于将范式归结为本科学生的大脑的设计结构,所以他们不会开始抱怨他们应该使用的语言愚蠢† .
这个想法似乎基本上是用编程语言中的对象建模“真实世界的对象”.对于实际的编程问题,这可能是一个很好的方法,但只有在事实上可以在使用现实世界对象的方式与在程序中处理OO对象的方式之间进行类比时才有意义.
对于这样的动物例子来说,这是荒谬的.如果有的话,方法必须是“饲料”,“牛奶”,“屠宰”……但“运输”是用词不当,我会采取这种方式来实际移动动物,这更像是一种方法动物所处的环境,基本上只是作为访客模式的一部分.
另一方面,描述,输入和所谓的运输更简单.这些基本上是类型相关的常量或简单的纯函数.只有OO偏执狂和ddagger;批准使他们成为阶级方法.
如果你不尝试将它强制成类似于OO的东西,而只是在Haskell中保留(有用的类型化)数据,那么基本上只有数据的任何东西都会变得更简单.
因此,这个例子显然不会让我们进一步让我们考虑OOP确实有意义的事情. Widget工具包浮现在脑海中.就像是
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的功能几乎简化为一个简单的列表.那么为什么不在需要容器的地方使用列表呢?
当然,在这个例子中,你可能会发现你确实需要异构容器;获得它们的最直接方法是{ – #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非常自然,值得将其压入语言中.
†抱歉,这个主题总是让我陷入强烈舆论咆哮模式……
& ddagger;这些偏执狂的部分原因是可变性的麻烦,这些麻烦在Haskell中没有出现.
标签:c,haskell,functional-programming,oop,virtual-functions 来源: https://codeday.me/bug/20190925/1815947.html