只要不包含任何奇怪的函数,例如map
,filter
,foldr
或其中的任何合成,我就很擅长推断lambda表达式的类型。但是,一旦我有类似的东西
\x y -> map x (y (. x))
我完全迷路了,无法终生解决不使用ghci的类型。
任何帮助将不胜感激
谢谢
答案 0 :(得分:2)
我对“奇怪”的理解是,您的意思是高阶函数。此表达式包含两个:map :: (a -> b) -> [a] -> [b]
和(.) :: (b -> c) -> (a -> b) -> a -> c
。它也是一个lambda,因此本身可能是一个高阶函数。此处带括号的箭头是函数参数的类型。
map
显示y
必须返回x
接受为参数的项目列表。因此它们具有部分签名x :: _yitem -> _outeritem
和y :: _yarg -> [_yitem]
,其中此map
的返回值是[_outeritem]
类型。请注意,我们尚不知道这些通配符中可以容纳多少个箭头。
(. x)
转换为\l -> l . x
,后者转换为\l r -> l (x r)
。整个lambda是适合y
的参数,因此y
是一个高阶函数。 l
必须接受x
的返回值。该名称具有l :: _outeritem -> _lret
和(. x) :: (_outeritem -> _lret) -> _xarg -> _lret
的名称,因为r
被用作x
的参数。哦,_xarg
因映射为_yitem
而为人所知。
好吧,这本身就是一堆令人困惑的步骤,所以让我们对结果进行排队:
type OuterLambda = _xtype -> _ytype -> MapRet
x :: _yitem -> _outeritem
type MapRet = [_outeritem]
y :: YArg -> [_yitem]
type YArg = (_outeritem -> _lret) -> _yitem -> _lret
y :: ((_outeritem -> _lret) -> _yitem -> _lret) -> [_yitem]
进步!它具有往返x
和y
的每种类型的名称。但是我们的表达是lambda,因此我们必须接受这两个:
(_yitem -> _outeritem) ->
(((_outeritem -> _lret) -> _yitem -> _lret) -> [_yitem]) ->
[_outeritem]
这是一种非常长的类型。让我们将其与山本雄二向我们展示的编译器推断类型进行比较:
(a0 -> b0) ->
(((b0 -> c0) -> a0 -> c0) -> [a0]) ->
[b0]
它匹配。我们在这里有很多功能顺序:表达式需要功能x
和y
,而y
需要功能本身具有l
功能。而且,我们确实为之命名的所有类型可能反过来变得非常复杂。
答案 1 :(得分:0)
注释故意错误的类型(通常为()
)会为您提供帮助。
例如:
> (\x y -> map x (y (. x))) :: ()
<interactive>:1:2: error:
• Couldn't match expected type ‘()’
with actual type ‘(a0 -> b0)
-> (((b0 -> c0) -> a0 -> c0) -> [a0]) -> [b0]’
• The lambda expression ‘\ x y -> map x (y (. x))’
has two arguments,
but its type ‘()’ has none
In the expression: (\ x y -> map x (y (. x))) :: ()
In an equation for ‘it’: it = (\ x y -> map x (y (. x))) :: ()
这篇文章中介绍了这个技巧:http://www.parsonsmatt.org/2018/05/19/ghcid_for_the_win.html