Haskell为另一个Either数据类型定义Functor实例 [英] Haskell defining Functor instance for an alternative Either data type

查看:125
本文介绍了Haskell为另一个Either数据类型定义Functor实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

遍历Typeclassopedia以获得使用类型类的一些路由.想要替代EitherFunctor实例,但是即使将Either的定义作为Functor的实例进行检查,也总是给我带来麻烦. 有这个,但是不会编译.

Going through Typeclassopedia to gain some routing working with type classes. Want to make an alternative to Either an instance of Functor, but even examining the definition of Either as an instance of Functor keeps getting me in trouble. Have this, but will not compile.

data Alt a b = Success a | Failure b deriving (Show, Eq, Ord) 

instance Functor (Alt a) where 
  fmap _ (Failure a) = Failure a
  fmap f (Success x) = Success (f x)  

    • Couldn't match expected type ‘a1’ with actual type ‘a’
      ‘a1’ is a rigid type variable bound by
        the type signature for:
          fmap :: forall a1 b. (a1 -> b) -> Alt a a1 -> Alt a b
        at Brenty_tcop.hs:25:3-6
      ‘a’ is a rigid type variable bound by
        the instance declaration
        at Brenty_tcop.hs:24:10-24
    • In the first argument of ‘f’, namely ‘x’
      In the first argument of ‘Success’, namely ‘(f x)’
      In the expression: Success (f x)
    • Relevant bindings include
        x :: a (bound at Brenty_tcop.hs:26:19)
        f :: a1 -> b (bound at Brenty_tcop.hs:26:8)
        fmap :: (a1 -> b) -> Alt a a1 -> Alt a b
          (bound at Brenty_tcop.hs:25:3)
   |
26 |   fmap f (Success x) = Success (f x) 

推荐答案

如@chepner所说注释,如果您切换类型参数的顺序,则会编译您的代码,

As @chepner says in the comments, your code will compile if you switch the order of the type parameters,

data Alt b a = Success a | Failure b

或切换Functor实例的意义,以便它映射到Failure并保留Success单独.

or alternatively switch the meaning of the Functor instance, so that it maps over Failure and leaves Success alone.

instance Functor (Alt a) where
    fmap f (Success x) = Success x
    fmap f (Failure x) = Failure (f x)

基本上,Functor类型类仅知道如何映射类型的最后一个类型参数.因此,我们必须重新调整内容,以便将函数f应用于最后一个类型参数的出现.

Basically, the Functor type class only knows how to map over a type's last type parameter. So we had to rejig things so that we apply the function f to an occurrence of that last type parameter.

为什么,您只能映射最右边的参数,这是一个非常深刻且有趣的问题.要了解这一点,您必须了解种类,这是Haskell类型系统的高级功能.

Why you can only map over the rightmost parameter is a very deep and interesting question. To understand this you have to understand kinds, which are an advanced feature of Haskell's type system.

从某种意义上讲,您可以将类型视为类型的下一级".类型对值进行分类;种类对类型进行分类.因此"foo"String,而String是类型.在Haskell中,类型"的发音为*.

You can think of kinds as being the "next level" of types, in some sense. Types classify values; kinds classify types. So "foo" is a String, and String is a type. In Haskell "type" is pronounced *.

-- :t in ghci asks for the type of a value-level expression
ghci> :t "foo"
"foo" :: String

-- :k asks for the kind of a type-level expression
ghci> :k String
String :: *

所有普通类型(可以具有值的类型)都具有*.所以String :: *Int :: *Bool :: *

All ordinary types - those which can have values - have a kind of *. So String :: *, Int :: *, Bool :: *, etc.

当您开始考虑参数化类型时,事情会变得很有趣. Maybe本身不是类型-您不能具有Maybe类型的值,但是可以具有Maybe IntMaybe String等.因此Maybe是一种函数-它需要一个类型作为参数,它产生一个类型. (Maybe类型构造函数,使用专业术语.)

Things get interesting when you start thinking about parameterised types. Maybe is not a type by itself - you can't have values of type Maybe, but you can have Maybe Int, Maybe String, etc. So Maybe is a sort of function - it takes a type as an argument and it produces a type. (Maybe is a type constructor, to use the technical term.)

-- Maybe is a function...
ghci> :k Maybe
Maybe :: * -> *

-- and you can apply it to an argument to get a type
ghci> :k Maybe Int
Maybe Int :: *

Alt是两参数类型的函数.就像常规值函数一样,在Haskell中使用类型函数进行查询,因此Alt的类型为* -> * -> *(这实际上表示* -> (* -> *)).

Alt is a two-parameter type function. Type functions are curried in Haskell, just like regular value functions, so Alt has a type of * -> * -> * (which really means * -> (* -> *)).

ghci> :k Alt
Alt :: * -> * -> *

现在,Functor高阶类型的函数.它需要一个参数f,它本身是一个类型函数. Functor本身不是有效的类型类约束,但Functor f是有效的.

Now, Functor is a higher-order type function. It takes an argument f, which itself is a type function. Functor on its own is not a valid type class constraint, but Functor f is.

ghci> :k Functor
Functor :: (* -> *) -> Constraint

这意味着Maybe本身具有* -> *类型,是Functor类型函数的有效参数.但是Int :: *不是,Maybe Int :: *也不是,Alt :: * -> * -> *也不是.错误消息告诉您种类不匹配:

This means Maybe on its own, with a kind of * -> *, is a valid argument for the Functor type function. But Int :: * isn’t, and nor is Maybe Int :: *, and nor is Alt :: * -> * -> *. The error messages tell you about the kind mismatch:

ghci> :k Functor Int
<interactive>:1:9: error:
    • Expected kind ‘* -> *’, but ‘Int’ has kind ‘*’
    • In the first argument of ‘Functor’, namely ‘Int’
      In the type ‘Functor Int’

ghci> :k Functor Alt
<interactive>:1:9: error:
    • Expecting one more argument to ‘Alt’
      Expected kind ‘* -> *’, but ‘Alt’ has kind ‘* -> * -> *’
    • In the first argument of ‘Functor’, namely ‘Alt’
      In the type ‘Functor Alt’

那里的种类系统可以防止您形成无效的类型,就像类型系统防止您写入无效的值一样.如果没有同类系统,并且允许我们编写instance Functor Alt,它将为fmap产生以下(荒谬的)类型:

The kind system is there to prevent you from forming invalid types, just like how the type system prevents you from writing invalid values. If there was no kind system, and we were allowed to write instance Functor Alt, it would produce the following (nonsensical) type for fmap:

-- `Alt a` is not a valid type, because its second argument is missing!
fmap :: (a -> b) -> Alt a -> Alt b

所以我们需要将Alt :: * -> * -> *转换为* -> *类型,以便为Functor提供有效的参数. Alt是一个咖喱类型函数,因此,如果我们给它一个单一的类型实参,我们将获得一个类型函数!

So we need to turn Alt :: * -> * -> * into something of kind * -> *, in order to have a valid argument for Functor. Alt is a curried type function, so if we give it a single type argument, we'll get a type function back!

ghci> :k Functor (Alt Int)
Functor (Alt Int) :: Constraint

这就是为什么instance声明为instance Functor (Alt x)的原因-它需要给Alt一个参数(在这种情况下,该参数可以是任何类型的x,只要其类型为*).现在我们有了fmap :: (a -> b) -> Alt x a -> Alt x b,它是有效的类型表达式.

That's why the instance declaration says instance Functor (Alt x) - it needs to give Alt an argument (and in this case the argument can be any type x as long as its kind is *). Now we have fmap :: (a -> b) -> Alt x a -> Alt x b, which is a valid type expression.

因此,通常来说,创建Functor实例的方法是先为您的类型提供参数,直到只剩下一个参数为止.这就是Functor只知道如何映射最右边的type参数的原因.作为练习,您可以尝试定义一个Functor类,该类映射到 second -last-type参数.

So in general, the recipe for making a Functor instance is to start by giving arguments to your type until it only has one parameter left. That's why Functor only knows how to map over the rightmost type parameter. As an exercise you can try defining a Functor class which maps over the second-to-last type parameter.

这是一个大话题,希望我不会太快.不立即了解种类也是可以的-我花了好几次尝试!在评论中让我知道您是否需要我进一步解释.

This is a big topic so hopefully I haven't gone too fast. It's OK not to understand kinds straight away - it took me several tries! Let me know in the comments if there's anything you'd like me to explain further.

这篇关于Haskell为另一个Either数据类型定义Functor实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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