[英]Applicative functors for my own data type (Haskell)
我试图理解haskell,但我遇到了“无法构造无限类型”的错误
我想为自己的数据类型实现“<*>”,模仿列表的行为。
到目前为止我的功能代码:
data List a = Nil | Cons a (List a) deriving Show
instance Functor (List) where
-- fmap :: (Functor f) => (a -> b) -> f a -> f b
fmap f Nil = Nil
fmap f (Cons a (b)) = Cons (f a) (fmap f b)
现在我正在尝试创建一个“应用列表”的实例:
instance Applicative List where
pure a = Cons a (Nil)
-- (<*>) :: f (a -> b) -> f a -> f b
(Cons a (b)) <*> (Cons c (d)) = Cons (fmap a (Cons c (d))) (b <*> (Cons c (d)))
(Nil) <*> _ = Nil
_ <*> (Nil) = Nil
目标是定义“<*>”,以便模拟列表的行为。 例子:
(fmap (*)) [5,6,3] <*> [0,2]
--> [0,10,0,12,0,6]
所以它应该创建:
(fmap (*)) (Cons 5 (Cons 6 (Cons 3 (Nil)))) <*> (Cons 0 (Cons 2 (Nil)))
--> (Cons 0 (Cons 10 (Cons 0 (Cons 12 (Cons 0 (Cons 6 (Nil))))))))
但不幸的是,我得到了一个(对我来说)非常无用的错误:
10-3.hs:14:65: error:
* Occurs check: cannot construct the infinite type: b ~ List b
Expected type: List (List b)
Actual type: List b
* In the second argument of `Cons', namely `(b <*> (Cons c (d)))'
In the expression: Cons (fmap a (Cons c (d))) (b <*> (Cons c (d)))
In an equation for `<*>':
(Cons a (b)) <*> (Cons c (d))
= Cons (fmap a (Cons c (d))) (b <*> (Cons c (d)))
* Relevant bindings include
b :: List (a -> b) (bound at 10-3.hs:14:14)
a :: a -> b (bound at 10-3.hs:14:11)
(<*>) :: List (a -> b) -> List a -> List b (bound at 10-3.hs:14:18)
|
14 | (Cons a (b)) <*> (Cons c (d)) = Cons (fmap a (Cons c (d))) (b <*> (Cons c (d)))
| ^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
我无法弄清楚为什么需要一个列表列表(List (List b)),因为我的数据类型的定义清楚地表明需要一个普通列表作为 Cons 的第二个参数。
我希望有人可以帮助我!
编辑:谢谢你解决了它。 现在这可能是题外话,但我试图复制用于列表的原始语法来解决它。 它在 Prelude 包中定义如下:
instance Applicative [] where
{-# INLINE (<*>) #-}
fs <*> xs = [f x | f <- fs, x <- xs]
因为我不能使用列表理解,所以我不想创建一个实际的列表(我以后可以转换它,但我不喜欢这个想法)。 我用 lambdaBot 翻译了语法糖并得到了这个:
fs <*> xs = concatMap (\ f -> concatMap (\ x -> [f x]) xs) fs
有没有办法做到这一点,或者这本质上是否等同于使用附加(助手)功能?
在违规行中:
(Cons a (b)) <*> (Cons c (d)) = Cons (fmap a (Cons c (d))) (b <*> (Cons c (d)))
子表达式fmap a (Cons c (d))
的类型为List b
,而您试图将其Cons
到(b <*> (Cons c (d)))
上,它也具有List b
的类型。 但请记住,类型是Cons :: a -> List a -> List a
。 注意Cons
的第一个元素需要是一个元素,第二个元素应该是一个列表。 因此,编译器假定您的元素类型本身是List b
,然后它报告它期望第二个参数的类型为List (List b)
。
要解决此问题,您应该编写一个append :: List a -> List a -> List a
function 并使用它,而不是使用Cons
:
(Cons a (b)) <*> (Cons c (d)) = append (fmap a (Cons c (d))) (b <*> (Cons c (d)))
关于语法的小提示:您可以像这样使代码更简洁:
Cons f fs <*> xs = append (fmap f xs) (fs <*> xs)
提示:
f
之类的暗示性名称来表示函数,并在末尾添加一个s
来表示某些东西的列表。(Cons c (d))
-> xs
。(b)
和(d)
等变量周围写括号。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.