Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusion with Haskell type inference

I don't understand why the following function works:

isLongerThanN :: Integral n => n -> [a] -> Bool
isLongerThanN n xs = length xs > fromIntegral n

but the following doesn't:

isLongerThanN' :: Integral n => n -> [a] -> Bool
isLongerThanN' n xs = length xs > n

which throws the error

Could not deduce (n ~ Int)
    from the context (Integral n)
      bound by the type signature for
                 isLongerThanN' :: Integral n => n -> [a] -> Bool
      at blah.hs:140:1-35
      `n' is a rigid type variable bound by
          the type signature for
            isLongerThanN' :: Integral n => n -> [a] -> Bool
          at blah.hs:140:1
    In the second argument of `(>)', namely `n'
    In the expression: length xs > n
    In an equation for `isLongerThanN'':
        isLongerThanN' n xs = length xs > n

(which I've likely misunderstood)

If anything, I would expect it to be the other way around, since fromIntegral is effectively broadening variable n's type.

like image 702
Inept Avatar asked Apr 29 '12 03:04

Inept


People also ask

Does Haskell support type inference?

Type inference is the process by which Haskell guesses the types for variables and functions, without you needing to specify these types explicitly. Many functional languages feature type inference. There is lots of theory behind type inference - Hindley-Milner type systems and Unification.

What does () mean in Haskell?

() is very often used as the result of something that has no interesting result. For example, an IO action that is supposed to perform some I/O and terminate without producing a result will typically have type IO () .


1 Answers

Consider the expression that doesn't work

isLongerThanN' :: Integral n => n -> [a] -> Bool
isLongerThanN' n xs = length xs > n

n can be any integer-y type, so it can be passed an Integer or Word or Int. (>) has type Ord a => a -> a -> Bool so both its left and right operand have to be of the same type. length xs returns an Int so this type has to be that. But, n can be any Integral, not necessarily Int, so we need some way of allowing n to be converted to an Int. This is what fromIntegral does (the fact that it also allows n to be any Num is basically irrelevant).

We could expand the working version to look like:

toInt :: Integral n => n -> Int
toInt = fromIntegral

isLongerThanN :: Integral n => n -> [a] -> Bool
isLongerThanN n xs = length xs > toInt n

which makes it clearer that we're using a specialised version of fromIntegral.

(Note that isLongerThanN n xs = fromIntegral (length xs) > n also works, because it allows the result of length to match up with the type of n.)

like image 69
huon Avatar answered Sep 30 '22 08:09

huon