我非常喜欢Haskell新手,如果答案很明显就道歉,但我正在通过Typeclassopedia努力更好地理解类别。在为Functors部分进行练习时,我遇到了这个问题:
举一个类型的例子* - > *不能成为的实例 Functor(不使用undefined)。
我的第一个想法是定义某种无限递归的fmap定义,但这与定义中使用的undefined
基本上不一样吗?
如果有人能够解释答案,我们将不胜感激。
谢谢!
原创练习的来源,第3部分:http://www.haskell.org/haskellwiki/Typeclassopedia#Introduction
答案 0 :(得分:22)
一个简单的例子是
data K a = K (a -> Int)
以下是ghci告诉我们的内容:我们尝试为Functor
自动派生K
个实例:
Prelude> :set -XDeriveFunctor
Prelude> data K a = K (a -> Int)
Prelude> :k K
K :: * -> *
Prelude> data K a = K (a -> Int) deriving Functor
<interactive>:14:34:
Can't make a derived instance of `Functor K':
Constructor `K' must not use the type variable in a function argument
In the data type declaration for `K'
问题是标准Functor
类实际上代表协变仿函数(fmap
将其参数提升为f a -> f b
),但是你无法做到撰写a -> b
和a -> Int
以获得类型b -> Int
的函数(请参阅Ramon的答案)。但是,可以为逆变函子定义一个类型类:
class Contravariant f where
contramap :: (a -> b) -> f b -> f a
并使K
成为它的一个实例:
instance Contravariant K where
contramap f (K g) = K (g . f)
有关Haskell中协方差/逆变的更多信息,请参阅here。
编辑:以下是来自Reddit的Chris Smith的a nice comment on this topic。
答案 1 :(得分:6)
扩展我的(简短)评论和米哈伊尔的回答:
鉴于(-> Int)
,您希望fmap
看起来像这样:
(a -> Int) -> (a -> b) -> (b -> Int)
或:
(a -> Int) -> (a -> b) -> b -> Int
很容易证明,从(a -> Int)
,(a -> b)
,b
三个参数中找不到Int
(没有undefined
)的可能方法,因此从(a -> Int)
,(a -> b)
无法到达(b -> Int)
。结论:Functor
没有(-> Int)
个实例。
答案 2 :(得分:0)
我也遇到了麻烦,我发现Ramon和Mikhail的答案很有用,谢谢!我将其放在答案中而不是评论中,因为500个字符太短,且无法进行代码格式化。
我在理解(a -> Int)
的协变量时遇到了麻烦,并提出了这个反例,显示data K a = K (a -> Int)
可以用作Functor的一个实例(证明Ramon的证明)>
data K a = K (a -> Int)
instance Functor K where
fmap g (K f) = K (const 0)
如果可以编译,则必须正确,对吗? ;-) 我花了一些时间尝试其他排列。翻转功能使其更容易:
-- "o" for "output"
-- The fmapped (1st) type is a function output so we're OK.
data K0 o = K0 (Int -> o)
instance Functor K0 where
fmap :: (oa -> ob) -> (K0 oa) -> (K0 ob)
fmap g (K0 f) = K0 (g . f)
将Int转换为类型变量,将其简化为3.2节练习1第2部分:
-- The fmapped (2nd) type argument is an output
data K1 a b = K1 (a -> b)
instance Functor (K1 a) where
fmap :: (b1 -> b2) -> K1 a b1 -> K1 a b2
fmap g (K1 f) = K1 (g . f)
强制将fmapped类型作为函数的参数是关键...就像Mikhail的回答所说,但现在我明白了;-)
-- The fmapped (2nd) type argument is an input
data K2 a b = K2 (b -> a)
instance Functor (K2 o) where
fmap :: (ia -> ib) -> (K2 o ia) -> (K2 o ib)
-- Can't get our hands on a value of type o
fmap g (K2 f) = K2 (const (undefined :: o))
-- Nor one of type ia
fmap g (K2 f) = K2 (const (f (undefined :: ia)))