首页 文章

了解Haskell中的函数定义和类型

提问于
浏览
2

我试图在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 的签名,我想知道我需要做什么来写一个具有该签名的功能 .

2 回答

  • 6

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

    关于你的第二个问题,虽然参数类型和返回类型都是相同的,但 a -> a 的类型不是真的 for all types a . 基于 map 的类型签名, idMap must 接受列表参数 . 我们可以为比必要的约束更多的函数声明类型签名,但不能更少 .

  • 3

    你的 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 确实具有相同的类型(与其元素一样),如您所料 .

相关问题