这是功能:
comboGraph :: [a] -> Int -> [b]
comboGraph _ 0 = []
comboGraph [] _ = []
comboGraph (x:xs) n =
(buildEdges x xs) : comboGraph xs n
where buildEdges h t = (h, comboGraph t (n-1))
Ths函数接收类型a
的列表,一个数字,并返回类型b
的列表。但是,正如您所看到的,类型b
实际上是递归类型 - 它将是[(a, [(a, b1)])]
的行。当我尝试编译时,我收到此错误:
• Couldn't match type ‘b’ with ‘(a, [b0])’
‘b’ is a rigid type variable bound by
the type signature for:
comboGraph :: forall a b. [a] -> Int -> [(a, [b])]
at xxx.hs:15:15
Expected type: [(a, [b])]
Actual type: [(a, [(a, [b0])])]
• In the expression: (buildEdges x xs) : comboGraph xs n
In an equation for ‘comboGraph’:
comboGraph (x : xs) n
= (buildEdges x xs) : comboGraph xs n
where
buildEdges h t = (h, comboGraph t (n - 1))
如何正确注释此功能?
答案 0 :(得分:5)
为了使问题更加明显,让我们在定义的最后一个案例中替换buildEdges
的定义:
comboGraph (x:xs) n =
(x, comboGraph xs (n-1)) : comboGraph xs n
comboGraph
的结果应该是一个列表,但其元素是一对,其中也有comboGraph
个结果(即相同类型的列表)。正如您所说的类型错误所说,这不起作用 - 就像您想要一个有两个尾巴的列表一样。该修复程序正在切换到反映您尝试执行的操作的不同数据结构:
-- Feel free to substitute better names.
data Combo a = Empty | Node a (Combo a) (Combo a)
deriving (Eq, Ord, Show)
Empty
涵盖了用于生成空列表的基本案例,而Node
为您希望在递归案例中组合的每个事物都有一个适当类型的字段。 comboGraph
然后成为:
comboGraph :: [a] -> Int -> Combo a
comboGraph _ 0 = Empty
comboGraph [] _ = Empty
comboGraph (x:xs) n = Node x (comboGraph xs (n-1)) (comboGraph xs n)
(注意Combo
实际上是一个二叉树,在节点上有值。)
答案 1 :(得分:4)
我喜欢其他答案,我认为你应该使用它。但它使得一些推理飞跃需要一些直觉,并且如果不用机械方式做几次就很难获得这种直觉。因此,在这个答案中,我将展示如何从一个失败的定义开始,如你所拥有的那样,"转动曲柄",并机械地获得一个有效的解决方案。以下技术可应用于任何无限类型错误。
你有以下条款(稍微转述):
comboGraph (x:xs) n =
(x, comboGraph xs (n-1)) : {- ... -}
只是做一些直截了当的类型推理推理,我们可以看到comboGraph
采用某种类型的列表(来自它在x:xs
上匹配模式的事实)和一个数字(来自它的事实)减去一个)。让我们为列表元素挑选一个具体的(单态的但尚未知道的)类型a
,看看我们可以推断出它返回的内容。
好吧,它清楚地返回一个里面有元组的列表。而元组的第一部分只是a
。那第二部分呢?元组的第二部分是...... comboGraph
返回的类型。因此,comboGraph
会返回满足等式的类型t
:
t = [(a, t)]
这个等式的唯一解决方案是[(a, [(a, [(a, [(a, ...)])])])]
。这种无限类型在Haskell中并不存在。但是有一个标准的技巧可以非常接近:通过引入一个newtype来使用(类型级别)递归。我们正在为t
求解,但Haskell类型必须以大写字母开头,因此我们将这个等式命名为T
。
newtype T a = T [(a, T a)] deriving Show
现在我们完全拥有T a ~ [(a, T a)]
,但我们确实有同构现象:即\(T xs) -> xs :: T a -> [(a, T a)]
和T :: [(a, T a)] -> T a
是反转的。所以现在我们可以通过利用这个同构来编写你的comboGraph
定义。让我们来命名同构的另一半:
unT :: T a -> [(a, T a)]
unT (T xs) = xs
所以:
comboGraph (x:xs) n =
T ((x, comboGraph xs (n-1)) : unT (comboGraph xs n))
基本案例必须包含在T
中,当然:
comboGraph _ 0 = T []
comboGraph [] _ = T []
在ghci中尝试:
> comboGraph "abc" 3
T [('a',T [('b',T [('c',T [])]),('c',T [])]),('b',T [('c',T [])]),('c',T [])]