【问题标题】:Type classes vs algebraic data types?类型类与代数数据类型?
【发布时间】:2017-11-28 23:32:46
【问题描述】:

我经常开始考虑要定义的类型类的问题,并意识到当我开始编码时,我不需要类型类而是可以用代数数据类型来解决我的问题,这似乎更简单。因此,我想知道什么时候需要类型类。

据我了解,类型类是一种表示特定类型存在特定函数的方式。例如,当类型 MyType 是 Monoid 的一个实例时,我可以使用函数mempty :: MyTypemappend :: MyType -> MyType -> MyType,这样幺半群法则成立。

我们可以通过将 Monoid 定义为类型而不是类型类来实现代数数据类型:

data Monoid a = Monoid { mempty :: a
                       , mappend :: a -> a -> a}

然后通过定义Monoid MyType 类型的新值(通过将其声明为实例来完成类型类)来说类型 MyType 是一个幺半群:

monoidMyType :: Monoid MyType
monoidMyType = Monoid { mempty = ...
                      , mappend = \a b -> ... }

然后,我们可以编写对幺半群进行操作的函数,例如:

dummyFun :: Monoid a -> a -> a
dummyFun m x = mempty m x

并通过显式传递适当的“monoid 值”来使用这些函数:

result = dummyFun monoidMyType valueOfMyType

等效的最后两个步骤在类型类中会非常相似:

dummyFun :: (Monoid a) => a -> a
dummyFun x = mempty x

result = dummyFun valueOfMyType

我看到的唯一实质性区别是,对于代数数据类型,当我们调用函数dummyFun 时,必须显式传递幺半群值。虽然不必明确通过它更实际一点,但在我看来这不是一个主要障碍。

事实上,我看到了代数数据类型相对于类型类的一个优势:您可以将不同函数中的类型关联起来:

data Bla a b = Bla {f1 :: a -> b, f2 :: b -> a, ...}

使用类型类执行此操作(我相信)需要使用多参数类型类扩展。

是否有理由使用我在这里没有看到的类型类?

在设计软件时,您是否可以交替选择使用类型类或代数数据类型,或者在某些情况下您不能没有类型类?

【问题讨论】:

  • 类型类和 ADT 是非常正交的概念。

标签: haskell


【解决方案1】:

你刚刚发明了类型类!一个类一个函数字典。在编译过程中,像

这样的代码
class Monoid a where
    mempty :: a
    mappend :: a -> a -> a

instance Monoid [a] where
    mempty = []
    mappend = (++)

mconcat :: Monoid a => [a] -> a
mconcat = foldr mappend

main = print $ mconcat ["foo", "bar"]

被翻译成明确的字典传递风格。

data Monoid a = Monoid { mempty :: a, mappend :: a -> a -> a }

list_monoid = Monoid [] (++)

mconcat :: Monoid a -> [a] -> a
mconcat monoid = foldr (mappend monoid)

main = print $ mconcat list_monoid ["foo", "bar"]

这种翻译正是类型类和字典之间最重要的区别:类是隐式的。您不必显式地传递 monoid 变量 - 编译器会为您处理管道。手动构建像Ord a => Ord [a] 这样的组合实例会特别乏味。

类和字典之间还有另一个关键区别,那就是连贯性。基本上,总是最多有一个“最佳”实例来满足给定的约束,该实例是全局且唯一的,您不能覆盖它。另一方面,对于字典传递样式,该函数将只使用您传入的任何字典,并且不能保证唯一性。 This is sometimes good and sometimes bad.

【讨论】:

  • “手动构建像 Ord a => Ord [a] 这样的组合实例会特别乏味”是什么意思?你能举个例子吗?
猜你喜欢
  • 2010-11-12
  • 1970-01-01
  • 2014-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-22
  • 2023-04-11
  • 1970-01-01
相关资源
最近更新 更多