如何在没有 ghci 的情况下找出 haskell 表达式的类型?

How do I find out the type of a haskell expression without ghci?

我很擅长推断 lambda 表达式的类型,只要它没有任何奇怪的函数,例如 mapfilterfoldr 或任何组合在里面。但是,一旦我有了

\x y -> map x (y (. x))

我完全迷路了,我这辈子都想不出如何在不使用 ghci 的情况下找出类型。

如有任何帮助,我们将不胜感激

谢谢

故意注释错误的类型(通常 ())会对你有所帮助。
例如:

> (\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))) :: ()

此技巧在post中介绍:http://www.parsonsmatt.org/2018/05/19/ghcid_for_the_win.html

我认为 "weird" 你的意思是高阶函数。此表达式包含两个:map :: (a -> b) -> [a] -> [b](.) :: (b -> c) -> (a -> b) -> a -> c。它也是一个 lambda,所以它本身很可能是一个高阶函数。这里每个带括号的箭头都是函数参数的类型。

map 表明 y 必须 return 一个 x 接受的项目列表作为参数。所以他们有部分签名 x :: _yitem -> _outeritemy :: _yarg -> [_yitem],其中这个 map 的 return 值是 [_outeritem] 类型。请注意,我们还不知道有多少箭头适合这些通配符。

(. x) 转换为 \l -> l . x 转换为 \l r -> l (x r)。整个 lambda 是一个适合 y 的参数,因此 y 是一个高阶函数。 l 必须接受来自 x 的 return 值。它有一个名称,因此 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]

进步了!这具有来自 xy 的每种类型的名称。但是我们的表达式是 lambda,所以我们必须接受这两个:

(_yitem -> _outeritem) -> 
(((_outeritem -> _lret) -> _yitem -> _lret) -> [_yitem]) ->
[_outeritem]

这是一种很长的类型。让我们将它与 Yuji Yamamoto 向我们展示的编译器推断类型进行比较:

(a0 -> b0) -> 
(((b0 -> c0) -> a0 -> c0) -> [a0]) -> 
[b0]

匹配。我们这里有相当多的函数顺序:表达式需要函数 xy,而 y 需要一个本身带有 l 函数的函数。我们确实有名称的所有类型都可能是任意复杂的。