[Haskell-beginners] confusing type signature with sections

Keshav Kini keshav.kini
Wed Oct 2 00:47:35 UTC 2013


Patrick Redmond <plredmond at gmail.com> writes:
> I wrote code to shift and scale the range [-1, 1] to [0, 1] and mapped
> it on a list.
>
>     Prelude> map (\n -> ((n + 1) / 2)) [-1, 0, 1]
>     [0.0,0.5,1.0]
>
> I thought using sections would better express the simplicity of this operation.
>
>     Prelude> :t (+ 1)
>     (+ 1) :: Num a => a -> a
>
>     Prelude> :t (/ 2)
>     (/ 2) :: Fractional a => a -> a
>
> Given the types of those sections, I thought it sensible for me to
> compose them like this.
>
>     Prelude> :t ((+ 1) / 2)
>     ((+ 1) / 2) :: (Fractional (a -> a), Num a) => a -> a
>
> This didn't work of course, and its type baffles me. Though it ends
> with "Num a) => a -> a", it gives an error when given a number. I
> don't understand what "(Fractional (a -> a), ...) =>" really means. It
> seems like I've asked Haskell to perform "/" on the arguments "(+ 1)"
> and "2", but that ought to be disallowed by the type of "/".

Well, what is the type of (/) ?

    Prelude> :t (/)
    (/) :: Fractional a => a -> a -> a

By writing ((+ 1) / 2), you are indeed dividing (+ 1) by 2.  You can
divide any two things of some type a, as long as that type is a
fractional type.  Here your two things are (+ 1), which is of type (Num
a) => a -> a, and 2, which is of type (Num a') => a'.  (Here I've added
prime symbols to distinguish between the type variables in the two type
signatures, as they are not necessarily the same when you put the two
expressions together into the same context.)

Now, because we are writing ((+ 1) / 2) and we know that (/) takes two
arguments that must be of the same type, we know that the type (Num a')
=> a' -> a' and the type (Num a'') => a'' have to be the same type, so
it must be that a' = a -> a, so now we have:

    (+ 1) :: (Num a, Num (a -> a)) => a -> a
    2 :: (Num a, Num (a -> a)) => a -> a

Furthermore, since (/) not only takes two arguments that are of the same
type but also requires that this type must be Fractional, we actually
have

    (+ 1) :: (Num a, Num (a -> a), Fractional (a -> a)) => a -> a
    2 :: (Num a, Num (a -> a), Fractional (a -> a)) => a -> a

But the Fractional typeclass actually is a subset of the Num typeclass
(that is, a type must be a Num type before it can be a Fractional
type). So the first "Num (a -> a)" is redundant. Thus we finally get

    (+ 1) :: (Num a, Fractional (a -> a)) => a -> a
    2 :: (Num a, Fractional (a -> a)) => a -> a
    (/) :: (Num a, Fractional (a -> a)) => (a -> a) -> (a -> a) -> a -> a
    ((+ 1) / 2) :: (Num a, Fractional (a -> a)) => a -> a
    
Hopefully that makes more sense.

-Keshav




More information about the Beginners mailing list