我创建了一个小函数,将整数列表映射到它们的平方根。代码本身很简单:
f x = map sqrt [1..x]
Run Code Online (Sandbox Code Playgroud)
使用默认类型推断,它可以成功加载,但该函数可以接受Floating类型类值,而我只是希望它接受Integers。所以我在它上面添加了一个类型注释。
f :: (Integral a, Floating b) => a -> [b]
f x = map sqrt [1..x]
Run Code Online (Sandbox Code Playgroud)
出乎我的意料,加载失败。在 GHCi 的 REPL 中抛出错误:
1.hs:48:7: error:
• Couldn't match type ‘a’ with ‘b’
‘a’ is a rigid type variable bound by
the type signature for:
f :: forall a b. (Integral a, Floating b) => a -> [b]
at 1.hs:47:1-41
‘b’ is a rigid type variable bound by
the type signature for:
f :: forall a b. (Integral a, Floating b) => a -> [b]
at 1.hs:47:1-41
Expected type: [b]
Actual type: [a]
• In the expression: map sqrt [1 .. x]
In an equation for ‘f’: f x = map sqrt [1 .. x]
• Relevant bindings include
x :: a (bound at 1.hs:48:3)
f :: a -> [b] (bound at 1.hs:48:1)
|
48 | f x = map sqrt [1..x]
| ^^
Run Code Online (Sandbox Code Playgroud)
我完全失去了理智,不知道错误在说什么。似乎抱怨结果类型应该是[a]而不是[b]. 但这是荒谬的,因为它a是类型 class 的成员Integral,并且该函数肯定会返回一个Floating数字列表。它只是没有任何意义。
谁能解释为什么会发生错误,我该如何解决?
问题是sqrt :: Floating a => a -> a总是将数字映射到相同类型的数字,注意a -> a类型签名中的 。它还要求数字是 的实例Floating,尽管从技术上讲,您可以创建同时是Integral和实例的类型Floating,但这没有多大意义。
如果您编写,[ 1 .. x ]则会生成与x. 因此,如果x是Integral a => a,则[ 1 .. x ]具有类型Integral a => [a](并且是相同的a)。但是,您的类型签名表示,对于每种Integral类型a,您都可以生成某个Floating类型的任何元素列表b。
您可以利用fromIntegral :: (Integral a, Num b) => a -> b将整数转换为另一种类型的数字,因此您可以将其写为:
f :: (Integral a, Floating b) => a -> [b]
f x = map (sqrt . fromIntegral) [1..x]Run Code Online (Sandbox Code Playgroud)