我已经尝试了无数的谷歌搜索答案,但对Haskell来说是新手,我不明白我发现的一半事情,而另一半只是没有完全相关。
我的问题是这个,如果我在ghci
中运行这些语句Prelude> let x = 5 :: (Num a) => a
Prelude> sqrt x
我得到了我的期望
2.23606797749979
但是,如果我把它放在一个文件中并进行编译(授予我在这里所做的事情非常简单)
sqrtNum :: (Num a, Floating b) => a -> b
sqrtNum x = sqrt x
我明白了
myfile.hs:2:18:
Could not deduce (a ~ b)
from the context (Num a, Floating b)
bound by the type signature for
sqrtNum :: (Num a, Floating b) => a -> b
at test2.hs:1:12-40
`a' is a rigid type variable bound by
the type signature for sqrtNum :: (Num a, Floating b) => a -> b
at test2.hs:1:12
`b' is a rigid type variable bound by
the type signature for sqrtNum :: (Num a, Floating b) => a -> b
at test2.hs:1:12
Relevant bindings include
x :: a (bound at test2.hs:2:9)
sqrtNum :: a -> b (bound at test2.hs:2:1)
In the first argument of `sqrt', namely `x'
In the expression: sqrt x
这个问题可能非常简单,我只是在监督它(因为这是我遇到的每一个其他错误的经历)但是这个没有点击。
提前致谢!
答案 0 :(得分:7)
你说的是你有从Num a到Floating b的功能。 sqrt函数需要浮动类型作为输入,但您只能保证Num。这是sqrt的类型:
Main> :t sqrt
sqrt :: Floating a => a -> a
所以,让我们复制sqrt,然后将它从浮动变为浮动:
sqrtNum :: (Num a, Floating a) => a -> a
sqrtNum x = sqrt x
我离开了Num,虽然只需要浮动。 Floating is a Fractional, and Fractional is a Num.
答案 1 :(得分:3)
如果这还不够清楚,那就让我们“偷看”。类型类是一种语法糖形式,其中Haskell在类型级别存储函数库。因此,类型类约束可以简单地实现为隐式传递的额外参数。例如,如果我写:
class Truthy a where
truthy :: a -> Bool
instance Truthy Integer where
truthy = (== 0)
instance Truthy Bool where
truthy = id
然后我们可以定义一个函数:
maybeTruthy :: Truthy x => x -> Maybe x
maybeTruthy x | truthy x = Just x
| otherwise = Nothing
这里的关键是我所做的与这个稍微复杂的代码完全相同:
data Truthifier x = Truthifier {truthify :: x -> Bool}
boolTruthifier :: Truthifier Bool
boolTruthifier = Truthifier id
integerTruthifier :: Truthifier Integer
integerTruthifier = Truthifier (== 0)
maybeTruthified :: Truthifier x -> x -> Maybe x
maybeTruthified lib x | truthify lib x = Just x
| otherwise = Nothing
唯一的区别是Haskell已经有效地绑定了boolTruthifier
到Bool
类型,因此我不需要将它明确地传递给maybeTruthy
,因为它“来了”用于“通过函数参数的类型Bool
”。上面我可以轻松定义notTruthifier = Truthifier not
,我可以开始使用maybeTruthified notTruthifier
代替maybeTruthified boolTruthifier
。
这解释了,例如,为什么Haskell默认禁止instance (Num a) => Truthy a
,你必须改为编写newtype TruthNum x = TruthNum x
然后instance (Num a) => Truthy (TruthNum a)
:Haskell必须有一个顶级类型来注册那个函数库用;如果你给它(Num a) => Truthy a
那么它没有比放在a
上更好的地方 - 也就是说,在所有函数上 - 而在后一种情况下它直接放在TruthNum
上}类型,仅当x
还有Num
函数的字典时才使用它。
sqrt
如何适应所有这些:在GHCi中,您会发现sqrt
的类型是:
Prelude> :t sqrt
sqrt :: Floating a => a -> a
换句话说,sqrt
可以对任何类型的值进行,如果该类型具有为其定义的Floating
字典。其sqrt
会返回相同类型的值 ,因为签名为a -> a
。
你试图写(Num a, Floating b) => a -> b
。这是一种比定义实际允许的类型更宽松的类型。