类型类与代数数据类型? [英] Type classes vs algebraic data types?

查看:83
本文介绍了类型类与代数数据类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I frequently start thinking about a problem in terms of type classes to be defined, and realize when I start coding that I don't need type classes and can solve my problem with algebraic data types instead, which seems more straightforward. As a result, I am wondering when type classes are necessary.

据我了解,类型类是一种表示某些类型存在某些功能的方式.例如,当类型MyType是Monoid的实例时,则可以使用函数mempty :: MyTypemappend :: MyType -> MyType -> MyType,以使monoid定律成立.

As I understand them, type classes are a way to say that certain functions exist for certain types. For example, when a type MyType is an instance of Monoid, then I can use the functions mempty :: MyType and mappend :: MyType -> MyType -> MyType, such that the monoid laws hold.

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

We could acheive the same with algebraic data types, by defining Monoid as a type rather than a typeclass:

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

,然后通过定义类型为Monoid MyType的新值(通过声明一个实例来完成)来说出MyType类型是一个monoid:

and then say that a type MyType is a monoid by defining a new value of type Monoid MyType (which with typeclasses is done by declaring it an instance):

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

然后,我们可以编写对像半体运算的函数,例如:

Then, we can write functions that operate on monoids like:

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

并通过显式传递适当的"monoid值"来使用这些功能:

And use those functions by explicitly passing the appropriate "monoid value":

result = dummyFun monoidMyType valueOfMyType

等效的最后两个步骤对于类型类将非常相似:

The equivalent last two steps would happen very similarly with typeclasses:

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

result = dummyFun valueOfMyType

我看到的唯一实质性区别是,对于代数数据类型,在调用函数dummyFun时必须显式传递monoid值.尽管不必显式地通过它比较实用,但是在我看来,它并不像是主要障碍.

The only substantial difference that I see is that with algebraic data types, the monoid value must be passed explicitly when we call the function dummyFun. Although it is a bit more practical not to have to pass it explicitly, it doesn't look to me like a major obstacle.

实际上,我看到了代数数据类型具有优于类型类的优点:您可以将跨不同功能的类型关联在一起

In fact, I see an advantage that algebraic data types have over type classes: you can relate together types accross different functions:

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

使用typeclasses进行此操作(我认为)需要使用多参数类型类扩展名.

Doing this with typeclasses would (i believe) require using the multiple parameter type classes extension.

有没有必要使用我在这里没有看到的类型类?

Is there a reason to use type classes that I'm not seeing here?

在设计软件时,您可以互换选择使用类型类或代数数据类型吗?还是存在不能没有类型类的情况?

When designing software, can you interchangeably chose to use type classes or algebraic data types, or are there situations where you can't do without type classes?

推荐答案

您刚刚发明了类型类!类功能字典.编译期间,类似

You just invented type classes! A class is a dictionary of functions. During compilation, code like

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"]

被翻译成显式的字典传递样式.

is translated into explicit dictionary-passing style.

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]这样的组合实例会特别繁琐.

That translation is exactly the most important difference between type classes and dictionaries: classes are implicit. You don't have to explicitly pass the monoid variable around - the compiler takes care of the plumbing for you. It would be especially tedious to build composed instances like Ord a => Ord [a] by hand.

类和词典之间还有另一个关键区别,那就是一致性.基本上,总是有最多一个最佳"实例来满足给定的约束,该实例是全局且唯一的,并且您无法覆盖它.另一方面,采用字典传递样式时,该函数将仅使用您传入的任何字典,并且不保证唯一性. 这有时好时坏.

There's one other key difference between classes and dictionaries, and that's coherence. Basically, there's always at most one "best" instance to satisfy a given constraint, that instance is global and unique, and you can't override it. With dictionary-passing style, on the other hand, the function will just use whatever dictionary you passed in and there are no guarantees of uniqueness. This is sometimes good and sometimes bad.

这篇关于类型类与代数数据类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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