【问题标题】:How do I find out the type of a haskell expression without ghci?如何在没有 ghci 的情况下找出 haskell 表达式的类型?
【发布时间】:2019-01-07 20:11:11
【问题描述】:

我非常擅长推断 lambda 表达式的类型,只要它没有任何奇怪的函数,例如 mapfilterfoldr 或其中的任何组合。但是,只要我有类似的东西

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

我完全迷路了,我一生都无法弄清楚如何在不使用 ghci 的情况下找出类型。

任何帮助将不胜感激

谢谢

【问题讨论】:

  • 通过自己进行类型推断。你能分享一下你的尝试吗?
  • 我尝试像这样重写表达式: \xyz -> map x (y (xz)) 然后 \xyz -> map x (y ( (.) xz)) 然后我尝试推断括号内表达式的类型,但我失败了。
  • 那么你不能执行这样的重写,因为它是(. x),它是\z -> (.) z x的缩写,所以重写是\x y -> map x (y (\z -> (.) z x))
  • 好吧,从最外层的函数map开始。它的类型是(a -> b) -> [a] -> [b],你知道x 的类型是a -> by (. x) 的类型是[a]。然后重复y
  • Hindley-Milner 的哪一部分你不明白? (Tongue firmly in cheek.)

标签: haskell lambda type-inference typing lambda-calculus


【解决方案1】:

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

map 表明y 必须返回x 接受作为参数的项目列表。所以他们有部分签名x :: _yitem -> _outeritemy :: _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]

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

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 我其实想用纸笔来解决这个问题
    • 为什么不直接输入:t \x y -&gt; map x (y (. x))
    • 这是一个我试图理解的练习
    • 我倾向于使用_ 作为类型孔; GHC 然后在“找到类型通配符”消息中报告推断的类型。 PartialTypeSignatures 使其成为警告而不是编译错误。
    • @Highness 这确实很有用。 Mark 的观点是,故意给出不正确的类型只是直接询问类型的一种不必要的迂回方式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-23
    相关资源
    最近更新 更多