简体   繁体   English

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

[英]Understanding function definitions and types in Haskell

I am trying to write a simple tool in Haskell as a learning exercise, and have encountered something that I cannot figure out. 我试图在Haskell中编写一个简单的工具作为学习练习,遇到了一些我无法弄清楚的东西。 Here is a simple sample illustrating it. 这是一个简单的例子来说明它。

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

main = do
    print $ idMap [1, 2]

According to my understanding, this example should compile and print [1, 2] when run. 根据我的理解,这个例子应该在运行时编译并打印[1, 2] However, if fails to compile with the following message: 但是,如果无法使用以下消息进行编译:

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)

It kind of makes sense, given that the signature of map is (a -> b) -> [a] -> [b] so the input type is not necessarily the same as the output type, but the signature of id is a -> a so surely it follows that map id would have a signature of (a -> a) -> [a] -> [a] ? 考虑到map的签名是(a -> b) -> [a] -> [b]所以输入类型不一定与输出类型相同,但id的签名是a -> a id a -> a肯定地说map id会有(a -> a) -> [a] -> [a]的签名吗?

The second part I don't really understand is why this is an exception given that all the types ( a and b as above) are Integer . 我不太明白的第二部分是为什么这是一个例外,因为所有类型(如上所述的ab )都是Integer It would make sense to me that since the signature of idMap is a -> a , there should only be a compile exception if it is used in a situation where the output type is expected to be different from input type. 对我来说有意义的是,由于idMap的签名是a -> a ,如果在输出类型与输入类型不同的情况下使用它,则应该只有编译异常。

Finally, how would I make this code actually work? 最后,我将如何使这段代码真正起作用? My real code is a little more complicated and I am relying on the output type matching the input type elsewhere in the code so I don't want to change the signature of idMap , I want to know what I would need to do to write a function with that signature. 我真正的代码有点复杂,我依赖于输出类型匹配代码中其他地方的输入类型,所以我不想更改idMap的签名,我想知道我需要做什么来写一个具有该签名的功能。

You are applying idMap to a list. 您正在将idMap应用于列表。 Therefore, we know the argument type should be some list type. 因此,我们知道参数类型应该是一些列表类型。 Further, you expect the return type to be a list ( [1,2] ), so the return type should be a list as well. 此外,您希望返回类型是一个列表( [1,2] ),因此返回类型也应该是一个列表。 Since the id function is fully polymorphic ( a -> a ), we can map it over a list of any type a and return a list of items of the same type a . 由于id函数是完全多态的( a -> a ),我们可以mapmap到任何类型a的列表,并返回相同类型a的项列表。 Thus, your final type signature should be [a] -> [a] . 因此,您的最终类型签名应为[a] -> [a]

Regarding your second question, while it's true that the argument type and return type are both the same, the type as a -> a is not true for all types a . 关于你的第二个问题,虽然参数类型和返回类型都是相同的,但对于所有类型a ,类型为a -> a不是真的。 Based on the type signature of map , idMap must accept a list argument. 根据map的类型签名, idMap 必须接受list参数。 We can declare type signatures for functions that are more constrained than necessary, but not less. 我们可以为比必要的约束更多的函数声明类型签名,但不能更少。

Your implementation of idMap ... 你的idMap实现......

idMap x = map id x

... involves applying map id on x . ...涉及在x上应用map id As a consequence, x must be a list. 因此, x必须是一个列表。

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

This will work just fine: 这将工作得很好:

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

Note that, thanks to the use of id , x and idMap x do have the same type (as do their elements), as you expected. 请注意,由于使用了idxidMap x确实具有相同的类型(就像它们的元素一样),正如您所期望的那样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM