Haskell乘以Int和实数

时间:2012-11-13 09:58:40

标签: haskell types int monomorphism-restriction real-datatype

我有

coefficient :: ???????
coefficient = 1.0

val :: Int

我想做

result :: ???????
result val coefficient = val * coefficient

我需要做哪些类型的签名和转换功能才能使其工作?如果我想有能力将val推广到任何类型的Num,我该怎么办呢?

此:

coefficient = 1.0

val :: Int
val = 3

result :: Num a => a
result = coefficient * (fromIntegral val)

给了我这个编译器警告:

Could not deduce (a ~ Double)
from the context (Num a)
  bound by the type signature for result :: Num a => a
  at Move.hs:17:1-41
  `a' is a rigid type variable bound by
      the type signature for result :: Num a => a at Move.hs:17:1
In the first argument of `(*)', namely `coefficient'
In the expression: coefficient * (fromIntegral val)
In an equation for `result':
    result = coefficient * (fromIntegral val)

我知道这不是我原来的问题,我在清理代码时犯了一些错误。

现在有一个系数类型:

coefficient :: Num a => a
coefficient = 1.0

val :: Int
val = 3

result :: Num a => a
result = coefficient * (fromIntegral val)

产生的错误:

Could not deduce (Fractional a) arising from the literal `1.0'
from the context (Num a)
  bound by the type signature for coefficient :: Num a => a
  at Move.hs:12:1-17
Possible fix:
  add (Fractional a) to the context of
    the type signature for coefficient :: Num a => a
In the expression: 1.0
In an equation for `coefficient': coefficient = 1.0

1 个答案:

答案 0 :(得分:13)

有一个函数fromIntegral,它将整数转换为任何其他数字类型。所以你可以这样做:

result :: (Integral n, Num m) => n -> m -> m
result val coefficient = fromIntegral val * coefficient

或者,以无点样式:

result = (*) . fromIntegral

更新有关更新的问题(@Drew)

考虑以下代码:

coefficient :: (Num a) => a
coefficient = 1.0

这本身无效,如下所示。因为1.0是分数(不是整数)的文字,所以GHC只能将其编码为能够表示分数的任何类型(forall a。分数a => a)。但是,您已指定它必须对任何数字类型有效(forall a。Num a => a)。某些数字类型(例如Integer)不能表示小数值,并且不是Fractional的实例(正确地说是这样),因此不能进行类型检查。您可以按如下方式解决此问题:

coefficient :: (Fractional a) => a
coefficient = 2.0

这里GHC可以推断出类型,系数工作正常。值得注意的是,Fractional是Num的子类,所以作为Fractional的所有东西也必须是Num。如果我们在我的答案的第一部分中查看函数,系数只需要是一个Num类型(因为我们只将它与(*)一起使用),所以我们可以使用这个系数定义来代替那个参数。出于同样的原因,您的问题就出现了。

result :: (Num a) => a
result = coefficient * fromIntegral val

同样,此函数的结果必须与系数的类型相同。由于系数不能是任何Num类型,而只是一个小数类型,我们需要将其更改为:

result :: (Fractional a) => a
result = coefficient * fromIntegral val

然后应该进行类型检查。 @singpolyma是正确的,你的原始错误部分与单同性限制有关,但你只需要使类型签名稍微具体一些。如果你想使用它(Num a)=> a,那么系数必须是整数(例如1)。

关于GHCi(@Marcin)的更新

为了在GHCi中使用它,我建议让GHCi推断出这种类型。如果在这种情况下你输入(在GHCi中):

let result val coefficient = fromIntegral val * coefficient

然后GHCi将正确推断结果的类型。您可以使用':t'命令向GHCi询问它认为的类型:

Prelude> :t result
result :: (Integral a1, Num a) => a1 -> a -> a

如果您必须拥有明确的类型签名,则可以执行以下操作:

let result = (\val coefficient -> fromIntegral val * coefficient) :: (Integral a, Num b) => a -> b -> b

尝试使用显式类型,但GHCi会使这个单态:

Prelude> :t result
result :: Integer -> Integer -> Integer

我们不想要的(这是因为类型注释引用了lambda表达式的值,而不是结果的声明)。我不知道如何让显式类型在这里工作,所以也许比我们更有知识的人可以回答:P