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

标签 haskell

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

有 if 语句(或 case):

这是某些函数中的代码的一部分:

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

这工作正常,但我有一个问题。

如何解决这个问题。

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

比如肉、香蕉等等。

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

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

由于在 haskell 中,我在对象中没有方法,因此无法将该方法放在对象本身上。

这个问题怎么解决。

最佳答案

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

我认为你所说的这一切的意思是面向对象程序员使用基类和继承来解决的问题:

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

变体类型通常比继承层次结构更容易、更安全,因为它完全已知构造函数可能会遇到什么。

然而,与“开放世界”相反,可以添加不同的新类型的对象绝对是一个现实的要求,并且这可以通过类来完成

类型类解决方案/存在性

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 实际上都有不同的类型,因此您不能传递包含例如 AppleOrange 。好吧,你无法比...

在面向对象中,它们也是不同的类型,但是您可以拥有实际指向派生对象的基类引用。 Haskell 不直接支持这一点,但有一个 GHC 扩展可以做到这一点:这称为存在类型,并且可以写成

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

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

请注意,这是 somewhat frowned upon ,仅当您确定这对您的应用程序来说是个好主意时才这样做。

“纯数据对象”解决方案

如果您实际上不需要类型区分,那么您应该考虑是否实际上需要任何不同的标签。为什么不直接去做

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

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

data Food = Food {foodVariety :: String, eat :: IO ()}

apple :: Int -> Food
apple i = Food "apple" $ crunch i

关于模块扩展中的 Haskell 多态性问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53083848/

相关文章:

haskell - 卡在 "Configurable"haskell 类中

haskell - 为什么 MC Haskell 函数会导致堆栈溢出

Haskell 用于 Lambda 演算、类型推断

java - Haskell 中的 undefined 和 Java 中的 null 有什么区别?

haskell - 如何避免重新计算具有相同参数的纯函数?

haskell - 在 haskell 中设置计数器并增加它

haskell - 共享值在 Haskell 中被(不必要地)多次评估

haskell - 如何在 Haskell 中使用 foldr 实现删除

performance - Haskell:代码运行太慢

parsing - Haskell 解析器到 AST