Haskell不能推断类型?

时间:2015-07-05 22:46:55

标签: haskell

我已经尝试了无数的谷歌搜索答案,但对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

这个问题可能非常简单,我只是在监督它(因为这是我遇到的每一个其他错误的经历)但是这个没有点击。

提前致谢!

2 个答案:

答案 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已经有效地绑定了boolTruthifierBool类型,因此我不需要将它明确地传递给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。这是一种比定义实际允许的类型更宽松的类型。