在类型类中使用类型构造函数有什么优势? [英] Any advantage of using type constructors in type classes?

查看:38
本文介绍了在类型类中使用类型构造函数有什么优势?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

例如,类 Functor :

class Functor a
instance Functor Maybe

这里也许是类型构造函数.

但是我们可以通过另外两种方式做到这一点:

But we can do this in two other ways:

首先,使用多参数类型类:

Firstly, using multi-parameter type classes:

class MultiFunctor a e
instance MultiFunctor (Maybe a) a

第二种使用类型族:

class MonoFunctor a
instance MonoFunctor (Maybe a)

type family Element
type instance Element (Maybe a) a

现在,后两种方法有一个明显的优势,那就是它允许我们执行以下操作:

Now there's one obvious advantage of the two latter methods, namely that it allows us to do things like this:

instance Text Char

或者:

instance Text
type instance Element Text Char

所以我们可以使用单态容器.

So we can work with monomorphic containers.

第二个优点是我们可以创建没有类型参数作为最终参数的类型的实例.可以说我们制作了 Ething 样式类型,但错误地放置了这些类型:

The second advantage is that we can make instances of types that don't have the type parameter as the final parameter. Lets say we make an Either style type but put the types the wrong way around:

data Silly t errorT = Silly t errorT

instance Functor Silly -- oh no we can't do this without a newtype wrapper

instance MultiFunctor (Silly t errorT) t

工作正常,并且

instance MonoFunctor (Silly t errorT)
type instance Element (Silly t errorT) t

也很好.

鉴于在类型类定义中仅使用完整类型(而不是类型签名)的灵活性优势,假设您使用的是GHC,并且不介意使用扩展,是否有任何理由使用原始样式定义?也就是说,您可以做一些特殊的事情来放置类型构造函数,而不仅仅是在多类类型类或类型族中无法做到的类型类中的完整类型吗?

Given these flexibility advantages of only using complete types (not type signatures) in type class definitions, is there any reason to use the original style definition, assuming you're using GHC and don't mind using the extensions? That is, is there anything special you can do putting a type constructor, not just a full type in a type class that you can't do with multi-parameter type classes or type families?

推荐答案

您的提案忽略了有关现有 Functor 定义的一些非常重要的细节,因为您没有仔细研究将要写的内容的细节.发生在类的成员函数中.

Your proposals ignore some rather important details about the existing Functor definition because you didn't work through the details of writing out what would happen with the class's member function.

class MultiFunctor a e where
    mfmap :: (e -> ??) -> a -> ????

instance MultiFunctor (Maybe a) a where
    mfmap = ???????

目前, fmap 的一个重要属性是它的第一个参数可以更改类型. fmap show ::(Functor f,Show a)=>f a->f字符串.您不能只是丢掉它,否则就失去了 fmap 的大部分价值.所以说真的, MultiFunctor 应该看起来更像...

An important property of fmap at the moment is that its first argument can change types. fmap show :: (Functor f, Show a) => f a -> f String. You can't just throw that away, or you lose most of the value of fmap. So really, MultiFunctor would need to look more like...

class MultiFunctor s t a b | s -> a, t -> b, s b -> t, t a -> s where
    mfmap :: (a -> b) -> s -> t

instance (a ~ c, b ~ d) => MultiFunctor (Maybe a) (Maybe b) c d where
    mfmap _ Nothing = Nothing
    mfmap f (Just a) = Just (f a)

请注意,尝试至少进行 close 推理变得非常复杂.所有功能依赖项均已就位,以允许实例选择而无需在各处注解类型.(我可能已经错过了几个可能的功能依赖项!)实例本身增加了一些疯狂的类型相等约束,以使实例选择更加可靠.最糟糕的是-与 fmap 相比,它的推理属性仍然较差.

Note just how incredibly complicated this has become to try to make inference at least close to possible. All the functional dependencies are in place to allow instance selection without annotating types all over the place. (I may have missed a couple possible functional dependencies in there!) The instance itself grew some crazy type equality constraints to allow instance selection to be more reliable. And the worst part is - this still has worse properties for reasoning than fmap does.

假设我以前的实例不存在,我可以这样写一个实例:

Supposing my previous instance didn't exist, I could write an instance like this:

instance MultiFunctor (Maybe Int) (Maybe Int) Int Int where
    mfmap _ Nothing = Nothing
    mfmap f (Just a) = Just (if f a == a then a else f a * 2)

这当然被打破了-但是它以一种前所未有的新方式被打破了. Functor 定义的真正重要部分是 fmap <中的 a b 类型/code>不会出现在实例定义中的任何位置.仅查看类就足以告诉程序员 fmap 不能的行为取决于类型 a b .您可以免费获得该保证.您无需相信实例已正确编写.

This is broken, of course - but it's broken in a new way that wasn't even possible before. A really important part of the definition of Functor is that the types a and b in fmap don't appear anywhere in the instance definition. Just looking at the class is enough to tell the programmer that the behavior of fmap cannot depend on the types a and b. You get that guarantee for free. You don't need to trust that instances were written correctly.

因为 fmap 为您提供了免费的保证,所以在定义实例时甚至不需要同时检查两个 Functor 律.检查法则 fmap id x == x 就足够了.当第一个定律被证明时,第二个定律是免费提供的.但是随着我刚刚提供的 mfmap 损坏,即使第二定律不是, mfmap id x == x 是真实的.

Because fmap gives you that guarantee for free, you don't even need to check both Functor laws when defining an instance. It's sufficient to check the law fmap id x == x. The second law comes along for free when the first law is proven. But with that broken mfmap I just provided, mfmap id x == x is true, even though the second law is not.

作为 mfmap 的实现者,您需要做更多的工作来证明您的实现是正确的.作为用户,您必须对实现的正确性给予更多信任,因为类型系统不能保证那么多.

As the implementer of mfmap, you have more work to do to prove your implementation is correct. As a user of it, you have to put more trust in the implementation's correctness, since the type system can't guarantee as much.

如果您为其他系统编写了更完整的示例,则会发现如果您想支持 fmap 的全部功能,它们同样会遇到很多问题.这就是为什么它们没有真正使用的原因.他们增加了很多的复杂性,只获得了很小的效用.

If you work out more complete examples for the other systems, you find that they have just as many issues if you want to support the full functionality of fmap. And this is why they aren't really used. They add a lot of complexity for only a small gain in utility.

这篇关于在类型类中使用类型构造函数有什么优势?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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