手动派生类型`(。)(foldr(++))(map(:))`

时间:2014-05-02 20:07:35

标签: haskell types ghci unification

我正在尝试派生(.) (foldr(++)) (map (:))

的类型

我首先推导出foldr (++)

的类型
foldr :: (a1 -> b1 -> b1) -> b1 -> [a1] -> b1
(++)  :: [a2] -> [a2] -> [a2]

a1 ~ [a2]
b1 ~ [a2]
b1 ~ [a2]

所以

foldr (++) :: [a2] -> [[a2]] -> [a2] ~ [a] -> [[a]] -> [a]

然后我推导出map (:)

的类型
map :: (a1 -> b1) -> [a1] -> [b1]
(:) :: a2 -> [a2] -> [a2]

a1 ~ a2
b1 ~ [a2] -> [a2]

所以

map (:) :: [a2] -> [[a2] -> [a2]] ~ [a] -> [[a] -> [a]]

最后是(.) (foldr(++)) (map (:))

的类型
(.) :: (b1 -> c1) -> (a1 -> b1) -> a1 -> c1
map (:) :: [a2] -> [[a2] -> [a2]]
foldr (++) :: [a3] -> [[a3]] -> [a3]

b1 ~ [a2]
c1 ~ [[a2] -> [a2]]
a1 ~ [a3]
b1 ~ [[a3]] -> [a3]

所以我得到

(.) (foldr(++)) (map (:)) :: a1 -> c1 ~ [a3] ->  [[a2] -> [a2]]

但如果我向GHCi询问:t (.) (foldr(++)) (map (:)),我会(.) (foldr(++)) (map (:)) :: [a] -> [[[a] -> [a]]] -> [[a] -> [a]]

哪个与我的结果不同,有什么帮助可以得到相同的结果?

谢谢,
安。

2 个答案:

答案 0 :(得分:9)

在最后一步中,你混淆论证oder。您将b1 -> c1map (:)类型统一,但您应将其与foldr (++)类型统一。

更一般地,您可能想学习如何自己调试这些复杂的计算。最后进行健全性检查是很好的(在这种情况下,通过使用ghci检查结果)。如果完整性检查不起作用,那么下一步就是说出或写下每个步骤是什么以及为什么要这样做。在这种情况下,您会说"然后我将b1 -> c1map (:)类型统一起来因为......",然后您会发现该错误,因为那里有'没理由这样做。

也许rubber duck debugging适合您,在那里您向橡皮鸭解释您的代码,并通过解释,您会看到代码的错误。

答案 1 :(得分:3)

我不太确定,但我认为您错过了为b1生成两个约束的事实,因为在合成f . gg的输出必须匹配f的输入。通过从这个事实开始,然后替换输入和输出,可以更容易地推断出合成的类型:

map (:)              :: [a] -> [[a] -> [a]]
foldr (++)           ::        [b         ] -> [[b         ]] -> [b         ]

[b] ~ [[a] -> [a]]
 b  ~  [a] -> [a]

foldr (++) . map (:) :: [a]                 -> [[b         ]] -> [b         ]
                      ~ [a]                 -> [[[a] -> [a]]] -> [[a] -> [a]]