模块扩展中的Haskell多态性问题

时间:2018-10-31 12:54:29

标签: haskell

在函数式编程和Haskell中,函数内部有大量代码。

带有if语句(或大小写):

这是某些功能代码的一部分:

case x of
    Apple i -> ...
    Orange i -> ...
    Tomato i -> ....

这很好,但是我有一个问题。

如何解决这个问题。

如果在库中使用此代码,并且每个开发人员都可以添加自己的类型。

像肉,香蕉等等。

以Haskell方式应在此函数中描述所有类型。

但这是不可能的,因为我不知道开发人员将添加什么。

由于在haskell中我没有对象中的方法,所以无法将方法放在对象本身上。

如何解决此问题。

1 个答案:

答案 0 :(得分:3)

如果这是C ++ / Java / etc。

我认为这一切都是OO程序员将通过基类和继承解决的事情:

class Food {
 private: int i;
 public: virtual void eat() = 0;
};

class Apple: public Food {public: void eat(){crunch(i);}};
class Orange: public Food {public: void eat(){squeek(i);}};
class Tomato: public Food {public: void eat(){splosh(i);}};
...
class Meat: public Food {public: void eat(){malm(i);}};  // added by other developer
...

现在,作为一般规则,您应该记住,此类OO类与Haskell类不同,并且可以更好地用变量类型表示-就像您在回答中所假定的那样。

变量类型解决方案

data Food = Apple Int | Orange Int | Tomato Int
eat :: Food -> IO ()
eat (Apple i) = crunch i
eat (Orange i) = squeek i
eat (Tomato i) = splosh i

与继承层次结构相比,变体类型通常更易于使用和安全,这恰恰是因为知道可能会遇到哪些构造函数。

但是,与“开放世界”相反,可以添加不同类型的新对象 绝对是现实的要求,而 可以通过类< / p>

类型分类解决方案/存在性

class Edible f where
  eat :: f -> IO ()

data Apple = Apple Int
instance Edible Apple where eat (Apple i) = crunch i
data Orange = Orange Int
instance Edible Orange where eat (Orange i) = squeek i
data Tomato = Tomato Int
instance Edible Tomato where eat (Tomato i) = splosh i
...
data Meat = Meat Int      --- added by other developer
instance Edible Meat where eat (Meat i) = malm i
...

与变体类型的主要区别在于,所有不同类型的Edible实际上具有不同的类型,因此您不能在包含Apple和{ {1}}个。好吧,您无法完成...

在OO中,它们也是不同的类型,但是您可以拥有基类引用,它们实际上指向派生对象。 Haskell不直接支持此功能,但是它有一个GHC扩展支持:这称为 existential类型,也可以编写为

Orange

{-# LANGUAGE GADTs #-}
data Food where
  Food :: Edible f => f -> Food

请注意,这是somewhat frowned upon,只有在您确定这对您的应用程序是个好主意时,才这样做。

“纯数据对象”解决方案

如果您实际上不需要类型区分,那么应该考虑是否真的需要任何其他标签 。为什么不做它

{-# LANGUAGE ExistentialQuantification, UnicodeSyntax #-}
data Food = ∀ f . Edible f => Food f

您仍然可以添加另一个字段来说明您正在处理的食物

data Food = Food {eat :: IO ()}

apple :: Int -> Food
apple i = Food $ crunch i
orange :: Int -> Food
orange i = Food $ squeek i
tomato :: Int -> Food
tomato i = Food $ splosh i
...
meat :: Int -> Food   -- added by other developer
meat i = Food $ malm i