类型类和重载,连接是什么? [英] Typeclasses and overloading, what is the connection?

查看:184
本文介绍了类型类和重载,连接是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正试图围绕类型类和实例进行封装,而我并不完全理解它们的重点。到目前为止,我对这个问题有两个问题:1)为什么当函数使用该类型的某个函数时,必须在函数签名中包含一个类型类。例如:

  f ::(Eq a)=> a  - > a  - > Bool 
fab = a == b

为什么要把(Eq a )签名。如果未为 a 定义 == ,那么为什么不在遇到 a时抛出错误== b ? 2)类型类和函数重载是如何相关的?


$ b $

$ b

不可能这样做:

 数据A = A 
数据B = B

f :: A - > A
f a = a

f :: B - > B
fb = b

但是可以这样做:

  data A = A 
data B = B

class F a其中
f :: a - > a

实例FA其中
fa = a

实例FB其中
fb = b

这是怎么回事?为什么我不能有两个具有相同名称的函数,但使用不同的类型......来自C ++我觉得很奇怪。但是我对这些东西的真正含义可能有错误的概念。但是一旦我把它们包装在这些类型实例的东西中,我就可以。



当我正在学习这些主题时,随意向我投掷类别或类型的理论词汇在学习Haskell的同时,我怀疑Haskell在这里做了些什么的理论基础。

解决方案

我同意大部分是 Willem Van Onsem的回答,但我认为它忽视了类型类比真正特定重载的主要优势之一:的抽象即可。想象一下,我们使用临时重载而不是类型类来定义 Monad 操作:

   - 也许
pure :: a - >也许
pure = Just

(>> =)::也许a - > (a - >可能b) - >也许b
只要x>> = f = fx
Nothing>> = _ = Nothing

- 或者
pure :: a - > ; e a
pure = Right

(>> =):: e a - > (a - > e b) - > eb
Right x>> = f = fx
Left err>> = _ = Left err



现在我们知道每个monad可以用 pure 和>> = ,如上所述,但我们也知道它们可以用 fmap pure 加入。因此,我们应该能够实现一个适用于任何 monad的加入函数:

 加入x = x>> = id 

但是,现在我们有一个问题。什么是加入的类型?



显然,加入必须是多态的,因为它可以通过设计工作在任何monad上。但给它的类型签名 forall m a。 m(m a) - > m a 显然是错误的,因为它不适用于所有类型,仅适用于单一类型。因此,我们需要一些类型的东西来表示需要存在一些操作(>> =):: m a - > (a - > m b) - > mb ,这正是类型类约束所提供的。



由此可见,特殊重载使得重载名称成为可能但是对那些重载名称进行抽象是不可能的,因为不能保证不同的实现以任何方式相关。您可以定义monads而不使用typeclass,但是当时您无法定义 join ,除非 mapM 序列以及所有其他当你定义两个操作时,你可以免费获得好东西。



因此,Haskell中需要使用类型类来实现代码重用并避免大量的重复。但是,你是否可以同时使用类型类重载和类型定向重载? ,事实上,Idris的确如此。但是伊德里斯的类型推断与Haskell的类型推断有很大不同,所以支持比在Haskell中支持Willem的答案更为可行。

I am currently trying to wrap my head around typeclasses and instances and I don't quite understand the point of them yet. I have two questions on the matter so far:

1) Why is it necessary to have a type class in a function signature when the function uses some function from that type class. Example:

f :: (Eq a) => a -> a -> Bool
f a b = a == b

Why put (Eq a) in the signature. If == is not defined for a then why not just throw the error when encountering a == b? What is the point in having to declare the type class ahead?

2) How are type classes and function overloading related?

It is not possible to do this:

data A = A
data B = B

f :: A -> A
f a = a

f :: B -> B
f b = b

But it is possible to do this:

data A = A
data B = B

class F a where
  f :: a -> a

instance F A where
  f a = a

instance F B where
  f b = b

What is up with that? Why can't I have two functions with the same name but operating on different types... Coming from C++ I find that very strange. But I probably have wrong conceptions about what these things really are. but once I wrap them in these type class instance thingies I can.

Feel free to hurl category or type theoretical words at me as well, as I am learning about these subjects in parallel to learning Haskell and I suspect there is a theoretical basis in these for how Haskell does things here.

解决方案

I agree with much of Willem Van Onsem’s answer, but I think it overlooks one of the principal advantages of typeclasses over truly ad-hoc overloading: abstraction. Imagine we used ad-hoc overloading instead of typeclasses to define the Monad operations:

-- Maybe
pure :: a -> Maybe a
pure = Just

(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Just x >>= f = f x
Nothing >>= _ = Nothing

-- Either
pure :: a -> Either e a
pure = Right

(>>=) :: Either e a -> (a -> Either e b) -> Either e b
Right x >>= f = f x
Left err >>= _ = Left err

Now, we know that every monad can be expressed in terms of pure and >>=, as above, but we also know that they can be equivalently expressed using fmap, pure, and join. Therefore, we should be able to implement a join function that works on any monad:

join x = x >>= id

However, now we have a problem. What is join’s type?

Clearly, join has to be polymorphic, since it works on any monad by design. But giving it the type signature forall m a. m (m a) -> m a would obviously be wrong, since it doesn’t work for all types, only monadic ones. Therefore, we need something in our type that expresses the need for the existence of some operation (>>=) :: m a -> (a -> m b) -> m b, which is exactly what the typeclass constraint provides.

Given this, it becomes clear that ad-hoc overloading makes it possible to overload names, but it is impossible to abstract over those overloaded names because there is no guarantee the different implementations are related in any way. You could define monads without typeclasses, but then you couldn’t define join, when, unless, mapM, sequence, and all the other nice things that you get for free when you define just two operations.

Therefore, typeclasses are necessary in Haskell to enable code reuse and to avoid enormous amounts of duplication. But could you have both typeclass-style overloading and type-directed, ad-hoc name overloading? Yes, and in fact, Idris does. But Idris’s type inference is very different from Haskell’s, so it’s more feasible to support than it is in Haskell for many of the reasons in Willem’s answer.

这篇关于类型类和重载,连接是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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