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

时间:2016-12-21 18:56:16

标签: function haskell types

我正在尝试在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的签名是{{}},这是有道理的。 {1}}因此a -> a签名为map id后肯定如此?

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

最后,我如何让这段代码真正起作用?我真正的代码有点复杂,我依赖于输出类型匹配代码中其他地方的输入类型,所以我不想更改a -> a的签名,我想知道我需要什么写一个带有该签名的函数。

2 个答案:

答案 0 :(得分:6)

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

关于你的第二个问题,虽然参数类型和返回类型都是相同的,但[a] -> [a]的类型不是真的对于所有类型{{ 1}}。根据{{​​1}}的类型签名,a -> a 必须接受列表参数。我们可以为比必要的约束更多的函数声明类型签名,但不能更少。

答案 1 :(得分:3)

您对idMap ...

的实施
idMap x = map id x

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

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

这样可以正常工作:

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

请注意,由于使用idxidMap x确实具有相同的类型(与其元素一样),如您所料。