4

我有

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
4

1 回答 1

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. Fractional a => a)。但是,您已指定它必须对任何数字类型都有效(forall a. Num a => a)。某些数字类型(例如整数)不能表示小数值,并且不是 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

于 2012-11-13T10:03:32.070 回答