编程语言
首页 > 编程语言> > c – Haskell中面向对象的编程

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