Haskell为另一个Either数据类型定义Functor实例 [英] Haskell defining Functor instance for an alternative Either data type
问题描述
遍历Typeclassopedia以获得使用类型类的一些路由.想要替代Either
的Functor
实例,但是即使将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 Int
,Maybe 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屋!