Haskell中面向对象的编程 [英] Object oriented programming in Haskell

查看:123
本文介绍了Haskell中面向对象的编程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解面向对象的风格编程Haskell,知道事情会因为缺乏可变性有点不同。我已经玩过类型类,但是我对它们的理解仅限于它们作为接口。所以我编写了一个C ++示例,这是一个纯粹的基础和虚拟继承的标准钻石。 蝙蝠继承飞行哺乳动物 $ c>飞行和哺乳动物继承 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
{returnI am a+ this-> transport()++ this-> type }

类飞行:virtual public Animal
{
public:
virtual std :: string transport()const;
};

std :: string Flying :: transport()const {returnFlying; }

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

std :: string Mammal :: type()const {returnMammal; }

class Bat:public Flying,public Mammal {};

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

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

$ p

解决方案

你只是不想这样做,甚至不开始。 OO肯定有其优点,但经典例子像你的C ++一样,几乎总是设计的结构设计,以打击本科生的大脑的范式,所以他们不会开始抱怨他们应该使用语言是多么愚蠢。 p>

这个想法似乎基本上是建模现实世界中的对象通过编程语言中的对象。对于实际的编程问题,这可能是一个很好的方法,但它只是有意义的,如果你可以实际上绘制一个类比如何使用真实世界的对象和OO对象在程序内处理。



这只是对这样的动物示例可笑的。如果有什么,这些方法必须是像饲料,牛奶,屠宰等东西,但运输是一个误称,我会采取实际上移动动物,这将是一个动物的生活环境的方法,基本上只是作为访问者模式的一部分。 p>

describe 键入 c> transport ,另一方面,更简单。这些基本上是类型相关的常量或简单的纯函数。只有OO paranoia 批准使他们的类方法。



任何东西沿着这条动物的东西,数据,如果您不尝试将其强制为某种类似OO的东西,而只是在Haskell中保留(有用地输入)数据,则变得更简单。



因为这个例子显然不会给我们带来任何进一步让我们考虑OOP 有意义的东西。小部件工具包出现在头脑中。像

 类Widget; 

类容器:public Widget {
std :: vector< std :: unique_ptr< Widget>>儿童;
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其中
childBoundaries :: c - > Int - >可能矩形

ReEquipable 它的方法实际上突变了容器。这在Haskell中是显而易见的 有问题。但是你也许会发现没有必要:如果你用简单列表替换了 Container 类,你可以将更新作为纯函数更新。



可能的,这对于手头的任务来说太低效了。完全讨论有效地进行可变更新的方法对于该答案的范围来说太多了,但是存在这样的方式。使用 透镜



摘要



OO对Haskell的翻译不太好。没有一个简单的通用同构,只有多个近似值之间的选择需要经验。尽可能经常地,你应该避免从OO角度完全处理问题,而是考虑数据,函数,monad层。事实证明,这让你在Haskell很远。只有在少数应用程序中,OO是如此的自然,所以值得将其加入语言。








> 这些偏执狂的部分原因是可变性的困难,这在Haskell中不会出现。


I'm trying to get an understanding of object oriented style programming in Haskell, knowing that things are going to be a bit different due to lack of mutability. I've played around with type classes, but my understanding of them is limited to them as interfaces. So I've coded up a C++ example, which is the standard diamond with a pure base and virtual inheritance. Bat inherits Flying and Mammal, and both Flying and Mammal inherit 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;
}

Basically I'm interested in how to translate such a structure into Haskell, basically that would allow me to have a list of Animals, like I could have an array of (smart) pointers to Animals in C++.

解决方案

You just don't want to do that, don't even start. OO sure has its merits, but “classic examples” like your C++ one are almost always contrived structures designed to hammer the paradigm into undergraduate students' brains so they won't start complaining about how stupid the languages are they're supposed to use.

The idea seems basically modelling “real-world objects” by objects in your programming language. Which can be a good approach for actual programming problems, but it only makes sense if you can in fact draw an analogy between how you'd use the real-world object and how the OO objects are handled inside the program.

Which is just ridiculous for such animal examples. If anything, the methods would have to be stuff like “feed”, “milk”, “slaughter”... but “transport” is a misnomer, I'd take that to actually move the animal, which would rather be a method of the environment the animal lives in, and basically makes only sense as part of a visitor pattern.

describe, type and what you call transport are, on the other hand, much simpler. These are basically type-dependent constants or simple pure functions. Only OO paranoia ratifies making them class methods.

Any thing along the lines of this animal stuff, where there's basically only data, becomes way simpler if you don't try do force it into something OO-like but just stay with (usefully typed) data in Haskell.

So as this example obviously doesn't bring us any further let's consider something where OOP does make sense. Widget toolkits come to the mind. Something like

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 { ... };

Why OO makes sense here? First, it readily allows us to store a heterogeneous collection of widgets. That's actually not easy to achieve in Haskell, but before trying it, you might ask yourself if you really need it. For certain containers, it's perhaps not so desirable to allow this, after all. In Haskell, parametric polymorphism is very nice to use. For any given type of widget, we observe the functionality of Container pretty much reduces to a simple list. So why not just use a list, wherever you require a Container?

Of course, in this example, you'll probably find you do need heterogeneous containers; the most direct way to obtain them is {-# LANGUAGE ExistentialQuantification #-}:

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

In this case Widget would be a type class (might be a rather literal translation of the abstract class Widget). In Haskell this is rather a last-resort thing to do, but might be right here.

Paned is more of an interface. We might use another type class here, basically transliterating the C++ one:

class Paned c where
  childBoundaries :: c -> Int -> Maybe Rectangle

ReEquipable is more difficult, because its methods actually mutate the container. That is obviously problematic in Haskell. But again you might find it's not necessary: if you've substituted the Container class by plain lists, you might be able to do the updates as pure-functional updates.

Probably though, this would be too inefficient for the task at hand. Fully discussing ways to do mutable updates efficiently would be too much for the scope of this answer, but such ways exists, e.g. using lenses.

Summary

OO doesn't translate too well to Haskell. There isn't one simple generic isomorphism, only multiple approximations amongst which to choose requires experience. As often as possible, you should avoid approaching the problem from an OO angle alltogether and think about data, functions, monad layers instead. It turns out this gets you very far in Haskell. Only in a few applications, OO is so natural that it's worth pressing it into the language.


Sorry, this subject always drives me into strong-opinion rant mode...

These paranoia are partly motivated by the troubles of mutability, which don't arise in Haskell.

这篇关于Haskell中面向对象的编程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆