【发布时间】:2020-05-03 20:24:27
【问题描述】:
我已经读完了LYAHFGG 中关于代数数据类型的第 8 章,当我尝试实现类似于 Scheme 的列表操作时遇到了障碍。
想法是尝试在 Pair adt 上构建 cons、car、cdr,然后编写标准递归来计算长度:
data Pair a b = NullPair | Pair { thisCar :: a, thisCdr :: b} deriving (Eq)
cons :: a -> b -> Pair a b
cons x y = Pair { thisCar = x, thisCdr = y}
car :: Pair a b -> a
car (Pair {thisCar = x, thisCdr = y}) = x
cdr :: Pair a b -> b
cdr (Pair {thisCar = x, thisCdr = y}) = y
instance (Show a, Show b) => Show (Pair a b) where
show NullPair = "()"
show (Pair { thisCar=x, thisCdr=y}) = "(" ++ show x ++ " . " ++ show y ++ ")"
到目前为止一切顺利:
l1 = NullPair -- ()
l2 = cons 3 NullPair -- (3)
l3 = cons (cons 2 NullPair) (cons 3 (cons 4 NullPair)) -- ((2) 3 4)
λ> l1
()
λ> l2
(3 . ())
λ> l3
((2 . ()) . (3 . (4 . ())))
λ> car l2
3
λ> car l3
(2 . ())
λ> cdr l2
()
λ> cdr l3
(3 . (4 . ()))
λ> cdr (cdr l3)
(4 . ())
请注意,当我输入 cdr (cdr l3) 时,REPL 没有抱怨。稍后会详细介绍...
所以这是我的长度函数(我们假设输入是一组嵌套对,其最里面的 thisCdr 是 NullPair)以及我在尝试编译它时遇到的错误。
len :: Pair a b -> Integer
len NullPair = 0
len p = 1 + len $ thisCdr p
lists.hs:117:19-27: error: …
• Couldn't match expected type ‘Pair a0 b0’ with actual type ‘b’
‘b’ is a rigid type variable bound by
the type signature for:
len :: forall a b. Pair a b -> Integer
at /home/nate/Documents/haskell/ProblemSets/lists.hs:115:8
• In the second argument of ‘($)’, namely ‘thisCdr p’
In the expression: 1 + len $ thisCdr p
In an equation for ‘len’: len p = 1 + len $ thisCdr p
• Relevant bindings include
p :: Pair a b
(bound at /home/nate/Documents/haskell/ProblemSets/lists.hs:117:5)
len :: Pair a b -> Integer
(bound at /home/nate/Documents/haskell/ProblemSets/lists.hs:116:1)
Compilation failed.
我的解释是,我告诉编译器寻找 Pair a b 类型的东西,但它找到了 b 类型的东西,并且不相信 b 实际上是 Pair a b 的替代品。同样令人费解的是,它对 cdr (cdr l3) 没有任何问题,即使 cdr 返回了一个 b 类型的值,但期望的是一个 Pair a b 类型的值。
所以:
- 谁能用技术术语解释这里发生了什么?显然我没有掌握关于类型系统的一些东西。或者很可能我的代码有缺陷。
- 这附近有吗?也许是执行此操作的更好方法 一种递归?
非常感谢您的帮助。
【问题讨论】:
-
你想让
len (cons True False)成为什么? -
@JosephSible-ReinstateMonica 就像我提到的那样,len 的输入是一个 Pair,其中包含 NullPair 作为其最里面的 thisCdr。所以,我不会在(cons True False)上运行 len。但是,我希望 len (cons True (cons False NullPair)) 返回 2。
-
但是来自
thisCdr :: b的类型变量不能保证是Pair类型。记住len只需要Pair类型。 -
"I won't run len on (cons True False)" 但是使用类型签名
len :: Pair a b -> Integer,您可以,而这正是编译器所关心的。因此,您需要重新考虑您的类型。