通过Typeclassopedia获得一些与类型类一起工作的路由。想让Either成为Functor的一个实例,但是即使把Either定义为Functor的一个实例也会让我陷入麻烦。有这个,但不会编译。
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) 发布于 2019-03-26 21:58:23
As @chepner says in the comments,如果您切换类型参数的顺序,您的代码将编译,
data Alt b a = Success a | Failure b或者,切换Functor实例的含义,使其映射到Failure上,而不让Success单独使用。
instance Functor (Alt a) where
fmap f (Success x) = Success x
fmap f (Failure x) = Failure (f x)基本上,Functor类型类只知道如何映射类型的最后一个类型参数。因此,我们必须重新编排内容,以便将函数f应用于最后一个类型参数的出现。
为什么只能映射到最右边的参数,这是一个非常深刻和有趣的问题。要理解这一点,您必须了解种类,这是Haskell类型系统的一个高级特性。
在某种意义上,你可以把种类看作是类型的“下一个层次”。类型分类值;类型分类类型。所以"foo"是一个String,String是一个类型。在Haskell中,"type“发音为*。
-- :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 :: *等等。
当你开始考虑参数化类型时,事情就变得有趣了。Maybe本身不是一种类型--您不能有Maybe类型的值,但是可以有Maybe Int、Maybe String等。所以Maybe是一种函数--它以类型作为参数,并生成类型。(Maybe是一个类型构造函数,使用技术术语。)
-- 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有一种类型的* -> * -> * (实际上是指* -> (* -> *))。
ghci> :k Alt
Alt :: * -> * -> *现在,Functor是一个高阶类型函数.它接受一个参数f,它本身就是一个类型函数。Functor本身不是有效的类型类约束,但Functor f是。
ghci> :k Functor
Functor :: (* -> *) -> Constraint这意味着Maybe本身具有某种* -> *,是Functor类型函数的有效参数。但是Int :: *不是,Maybe Int :: *也不是,Alt :: * -> * -> *也不是。错误消息告诉您哪种错配:
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生成以下(无意义的)类型
-- `Alt a` is not a valid type, because its second argument is missing!
fmap :: (a -> b) -> Alt a -> Alt b因此,我们需要将Alt :: * -> * -> *转化为某种类型的* -> *,以便为Functor提供一个有效的论据。Alt是一个匆匆的类型函数,所以如果我们给它一个类型参数,我们将得到一个类型函数!
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,它是一个有效的类型表达式。
因此,一般来说,创建Functor实例的方法是首先给类型提供参数,直到它只剩下一个参数。这就是为什么Functor只知道如何映射到最右边的类型参数。作为练习,您可以尝试定义一个Functor类,该类映射到第二个类型参数。
这是一个很大的话题,希望我没有走得太快。不马上就明白什么是没问题的--它花了我几次尝试!如果你想让我进一步解释的话,请在评论中告诉我。
https://stackoverflow.com/questions/55364861
复制相似问题