Haskell multiply Int and real number

2019-02-14 08:43发布

I have

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

and

val :: Int

and I would like to do

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

What type signatures and conversion functions do I need to do to make this work? What must I do on top of that if I want to have ability to generalize val to any kind of Num?

This:

coefficient = 1.0

val :: Int
val = 3

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

gives me this compiler warning:

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)

I know thats not what I asked originally, I made some mistakes when sanitizing my code.

Now with a type for coefficient:

coefficient :: Num a => a
coefficient = 1.0

val :: Int
val = 3

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

the resulting error:

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条回答
小情绪 Triste *
2楼-- · 2019-02-14 09:28

There is a function, fromIntegral, which will convert an integral number to any other numeric type. So you can do:

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

Or, in point-free style:

result = (*) . fromIntegral

Update about updated question(@Drew)

Consider this code:

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

This is invalid on it's own, as follows. Because 1.0 is a literal for a fractional number (not a whole integer), then GHC can only encode it as any type which is capable of representing fractional numbers (forall a. Fractional a => a). However, you have specified that it must be valid for any numeric type (forall a. Num a => a). Some numeric types (e.g. Integer) cannot represent fractional values, and are not instances of Fractional (rightly so), so this cannot typecheck. You can fix this as follows:

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

Here GHC can infer the type, and coefficient works fine. It is important to note, that Fractional is a subclass of Num, so everything that is a Fractional must also be a Num. If we look at the function in the first part of my answer, coefficient is only required to be a Num type (as we only use it with (*)), so we can use this definition of coefficient in place of that parameter. Your problem occurs for exactly the same reason.

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

Again, the result of this function must be of the same type as coefficient. As coefficient cannot be any Num type, but only a fractional type, we need to change this to:

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

And then that should typecheck. @singpolyma is right that your original error was partly to do with the monomorphism restriction, but you just needed to make the type signatures slightly more specific. If you want it to work with (Num a) => a, then coefficient must be a whole number (e.g. 1).

Update about GHCi (@Marcin)

For using this in GHCi, I would suggest letting GHCi infer the type. If in this case you type (in GHCi):

let result val coefficient = fromIntegral val * coefficient

Then GHCi will correctly infer the type of the result. You can ask GHCi what type it thinks something is using the ':t' command:

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

If you must have an explicit type signature you can do:

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

To try and have an explicit type, but GHCi will make this monomorphic:

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

Which we don't want (this is because the type annotation refers to the value of the lambda expression, not the declaration of result). I don't know how to get the explicit type to work here either, so maybe someone more knowledgeable than us can answer :P

查看更多
登录 后发表回答