【问题标题】:Understanding function definitions and types in Haskell了解 Haskell 中的函数定义和类型
【发布时间】:2016-12-21 18:56:16
【问题描述】:

我正在尝试在 Haskell 中编写一个简单的工具作为学习练习,但遇到了一些我无法弄清楚的事情。这是一个简单的示例来说明它。

idMap :: a -> a
idMap x = map id x

main = do
    print $ idMap [1, 2]

根据我的理解,这个例子应该在运行时编译并打印[1, 2]。但是,如果编译失败并显示以下消息:

source_file.hs:2:18: error:
    • Couldn't match expected type ‘[b0]’ with actual type ‘a’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          idMap :: forall a. a -> a
        at source_file.hs:1:10
    • In the second argument of ‘map’, namely ‘x’
      In the expression: map id x
      In an equation for ‘idMap’: idMap x = map id x
    • Relevant bindings include
        x :: a
          (bound at source_file.hs:2:7)
        idMap :: a -> a
          (bound at source_file.hs:2:1)

这有点道理,因为map 的签名是(a -> b) -> [a] -> [b],所以输入类型不一定与输出类型相同,但id 的签名是a -> a,所以它肯定遵循map id 的签名是 (a -> a) -> [a] -> [a]

我不太明白的第二部分是为什么这是一个例外,因为所有类型(ab 如上所述)都是Integer。对我来说,由于idMap 的签名是a -> a,这对我来说是有意义的,如果在预期输出类型与输入类型不同的情况下使用它,则应该只存在编译异常。

最后,我怎样才能让这段代码真正起作用?我的真实代码有点复杂,我依赖输出类型匹配代码中其他地方的输入类型,所以我不想更改idMap 的签名,我想知道我需要做什么用那个签名写一个函数。

【问题讨论】:

  • map id 的类型是[a] -> [a],而您将其指定为a -> a

标签: function haskell types


【解决方案1】:

您正在将idMap 应用于列表。因此,我们知道参数类型应该是某种列表类型。此外,您希望返回类型是一个列表 ([1,2]),因此返回类型也应该是一个列表。由于id 函数是完全多态的(a -> a),我们可以map 覆盖任何类型的列表a 并返回相同类型的项目列表a。因此,您的最终类型签名应该是[a] -> [a]

关于你的第二个问题,虽然参数类型和返回类型确实相同,但 a -> a 的类型并不是真的对于所有类型a。基于map 的类型签名,idMap 必须 接受列表参数。我们可以为那些比必要的约束更多的函数声明类型签名,但不能更少。

【讨论】:

    【解决方案2】:

    你对idMap的实现...

    idMap x = map id x
    

    ...涉及在x 上应用map id。因此,x 必须是一个列表。

    GHCi> :t map id
    map id :: [b] -> [b]
    

    这会很好用:

    idMap :: [a] -> [a]
    idMap x = map id x
    

    请注意,由于使用了 idxidMap x 确实具有相同的类型(正如您所期望的那样)。

    【讨论】:

    • 谢谢。这确实有点道理。但这是否意味着我总是需要尽可能明确地使用签名?我会认为a 只是意味着一个未知类型,它恰好是另一个未知类型的列表?
    • 类型签名a -> a意味着a可以是any类型,但是基于map的类型签名,这不是真的。这可能有助于您理解:stackoverflow.com/questions/12230820/…
    • @aquavitae 问题在于,正如 mnoronha 所指出的,idMap :: a -> a 等于声明您可以将任何内容传递给您的函数(甚至,例如,IntegerBool ),这是不正确的。鉴于您使用 map 的方式,[a] -> [a] 是可能的最通用类型(类型检查器可接受的更专业签名的示例是 idMap :: [Integer] -> [Integer])。
    猜你喜欢
    • 1970-01-01
    • 2014-05-04
    • 1970-01-01
    • 2012-02-22
    • 2016-06-01
    • 2011-01-20
    • 1970-01-01
    • 2018-02-05
    • 1970-01-01
    相关资源
    最近更新 更多