Search code examples
haskellghciapplicativeliftingderivingvia

Why does this expression have a valid type?


Banging around in ghci, I happened to notice that the expression (*) 1 [1..5] apparently has a valid type.

:t (*) 1 [1..5]
(*) 1 [1..5] :: (Enum t, Num [t], Num t) => [t]

Apparently it is a list with several type constraints, including Num [t] which looks impossible to me, like it should give an error.

How is this the type of the expression? Why does ghci's :t command not give an error here?


Solution

  • Let's look at how these constraints come to be to explain the type.

    Numbers

    In Haskell a literal number is replaced with a call to fromInteger (or fromRational if it has a decimal point or an 'e' in it). This way one can write '1' and have it be float or a double or an int or whatever. The type of fromInteger is

    fromInteger :: Num a => a
    

    So 1 gets desugared to fromInteger (1::Integer) which has type Num t => t

    Ranges

    In Haskell the syntax [a..b] is converted into the call enumFromTo a b and the type is enumFromTo :: Enum a => a -> a -> [a]. Putting these together we get

    [1..5] == enumFromTo (fromInteger 1) (fromInteger 5) :: (Enum a, Num a) => [a]
    

    Putting it all together

    Now the type of (*) is Num b => b -> b -> b so we combine these all together to get:

    (Num t,
    Num a,
    Enum a,
    Num b,
    t~b,
    [a]~b) => b
    

    Note that a~b means the types a and b are the same. Combining these gives the type

    (Num a, Enum a, Num [a]) => [a]