(更新:显然我的原始答案对 OP 不是很有帮助...请参阅下面的第二个 HR 以获取附录。)
在这种情况下,你真正想做的是启动ghci并使用:t找出各种表达式的类型。例如
:t foldr map
-- answer: foldr map :: [b] -> [b -> b] -> [b]
如果您需要先定义名称,请使用 let(在 Haskell 源文件中不需要):
let mys = undefined :: a -> a
:t map mys
-- answer: [a] -> [a]
注意undefined 的使用带有显式类型签名。找出各种形式的表达式类型是完全可以的,甚至可能在实际代码中用作早期规划阶段的占位符。
Num a => 是a 上的类型类约束;参见例如Type Classes and Overloading 来自 "A Gentle Introduction to Haskell, Version 98" 或 Chapter 6. Using Typeclasses 来自 "Real World Haskell" 以获取更多信息。 (基本上,它做你认为它做的事情。:-))
以上内容对于验证您的答案应该很有用(并且类型类的资源很好)。至于如何自己解决这类问题:
类型推断是所谓的“统一算法”的应用。谷歌“统一”了一些资源;如果您在查询中添加编程语言的名称,您可能会找到示例实现。
至于如何处理手头的例子......
一个。 map mys:map 接受 a -> b 类型的函数并返回 [a] -> [b] 类型的函数。一般来说,a 可以不同于b,但mys 的类型是a -> a,所以返回的函数将是[a] -> [a] 类型。
这里有一些手统一:
(a -> b) -> [a] -> [b]`
(a -> a)`
^-- at this point we unify a with b;
when propagated to the return type,
this produces [a] -> [a]
b. mys map:mys 是一个函数,它接受某种类型的对象并返回相同类型的对象。特别是,如果您将(a -> b) -> [a] -> [b] 类型的参数传递给它,那将是返回值的类型。
顺便说一句,只有一个“有趣的”(不是undefined)函数,其类型签名是a -> a(没有类型类约束),即id。请参阅 Philip Wadler 的论文“免费定理!” (您可以从this page 下载)以进行扩展讨论。
c。 foldr map:首先,注意foldr签名中的as和bs与map签名中的那些无关。将map 的a 重命名为c 并将b 重命名为d 并使用签名map :: (c -> d) -> [c] -> [d] 可能会很方便,以便在下面更清楚地看到这一点。另请注意,(a -> b -> b) 只是写(a -> (b -> b)) 的一种更简单的方式。
更多手统一(解释如下):
(a -> (b -> b))
(c -> d) -> [c] -> [d]
这里发生的是foldr 接受(a -> (b -> b)) 类型的函数参数。如果您将map 作为该参数传入,a 将与c -> d 统一,b 与[c] 统一,然后再次与[d] 统一,这意味着c 等于d。
foldr的返回值有b -> [a] -> b类型;替换上一段中获得的更具体的类型,我们得到
[c] -> [c -> c] -> [c]
^-- c equals d, right?
c 来自我们为map 更改的签名;用原来的b,就变成了
[b] -> [b -> b] -> [b]
d。 foldr map . mys:这其实是(foldr map) . mys,不是foldr (map . mys)——函数应用(“隐形算子”)绑定最强!结合一个。和 c。从上面解决这个问题留给读者作为练习。 ;-)