Dave
Dave

Reputation: 8641

Haskell. Type error when loading function but not inline

I'm a complete newbie to Haskell. I type this into WinGHCi and it works fine:

> let x = 0.5
> let n = 5
> map (\y->(x**y)) [0..n]
[1.0,0.5,0.25,0.125,6.25e-2,3.125e-2]  -- notice it is powers of 1/2 !

But when I define a simple function in a file:

powersOfx :: (Integral a, Floating b) =>  a -> b -> [b]
powersOfx n x = map (\y->(x**y)) [0..n]

and type :l myFile, I get:

 Couldn't match expected type ‘b’ with actual type ‘a’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          powersOfx :: forall a b. (Integral a, Floating b) => a -> b -> [b]

What's going on? Am I doing the signature incorrectly? I'm guessing I might be, because when I just comment it out and :l myFile it works but

:t powersOfx 

I get:

powersOfx :: (Floating b, Enum b) => b -> b -> [b]

Notice the 'Enum' instead of 'Integral'.

I suppose I could just get rid of the type signature but I'm under the impression it's good practice to put a signature AND I'm trying to solve a larger problem where I'm getting error reported here: Ambiguous type variable `a0' arising from a use of `it'

If I get this part working, I'll post a separate question about that!

Please let me know if I'd be better off posting to another group or if I should be posting more information.

-Dave

Upvotes: 1

Views: 100

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477617

The signature of the map (\y->(x**y)) [0..n] function

In short: your signature is too broad, one can design Floating types such that the function can not be called.

Let us first analyze the types you use here:

Prelude> let x = 0.5
Prelude> let n = 5
Prelude> :t map (\y->(x**y)) [0..n]
map (\y->(x**y)) [0..n] :: (Enum b, Floating b) => [b]
Prelude> :t \n x -> map (\y->(x**y)) [0..n]
\n x -> map (\y->(x**y)) [0..n]
  :: (Enum b, Floating b) => b -> b -> [b]

So what we see here is that the output type b, actually has to be an instance of two typeclasses: Enum, and Floating. That is logical: you use [0..n], so that means that you ask Haskell to enumerate all elements between 0 and n, but a number is not per se enumerable (in fact we can already wonder that Floating is Enumerable in the first place: here we make hops of one, but we thus omit the Floating values in between, in case we would have a real float, then enumerating is even impossible).

As a result the ys have the same type as n, and since we perform arithmetic with x and y (we write x ** y), this thus means that x and y need to have the same type since (**) has type (**) :: Floating a => a -> a -> a. We can thus construct a function:

powersOfx :: (Enum b, Floating b) => b -> b -> [b]
powersOfx n x = map (\y->(x**y)) [0..n]

But this is a bad idea. Floating is usually something we better avoid as much as possible: if the number is large, we can have all sorts of rounding errors. We could use fromIntegral :: (Integral a, Num b) => a -> b, but again this can result in rounding errors when we convert for example a large Int to a Float. In that case the type would be:

powersOfx :: (Enum b, Floating b, Integral a) => a -> b -> [b]
powersOfx n x = map (\y->(x**y)) [0..fromIntegral n]

Making a more generic variant

Nevertheless we can make the function more flexible and generic, by using iterate :: (a -> a) -> a -> [a] and by using the take :: Int -> [a] -> [a] (or genericTake :: Integral i => i -> [a] -> [a]):

import Data.List(genericTake)

powersOfx :: (Integral i, Num n) => i -> n -> [n]
powersOfx n x = genericTake (n+1) (iterate (x*) 1)

This then produces:

Prelude Data.List> f 5 0.5
[1.0,0.5,0.25,0.125,6.25e-2,3.125e-2]

Upvotes: 3

Related Questions