Evaluation, let and where in Haskell

haskell

Solution

The problem is that the `literal 200` is interpreted differently in the two different contexts with your first example. Think of it as

((:) :: a -> [a] -> [a])
    ((coerceInteger :: Integer -> Integer) (literal 100 :: Expr a => a))
    (literal 100 :: Expr a => a)

Just based off the types, the compiler determines that the first `literal 100` must have type `Integer` because it's being passed to `coerceInteger`, since it has to take a value of type `Integer`. This also sets the type of `(:)` to now be `Integer -> [Integer] -> [Integer]`, implying that the last `literal 100` has to have type `[Integer]`.

In the second example, you're saying that both of them have the same value, and therefore the same type, which is impossible because the second must be a list for `(:)` to type check.

This actually occurs because of the dreaded Monomorphism restriction. You can fix this problem in two ways: One, turn off the monomorphism restriction with `{-# LANGUAGE NoMonomorphismRestriction #-}`, or you can provide an explicit type to `expr` that keeps it generalized:

main :: IO ()
main = print $ coerceInteger expr : expr
    where
        expr :: Expr a => a
        expr = literal 100

Either of these approaches work, and whatever you decide to do I would recommend always providing type signatures to help avoid these problems.

In fact, once you add the type signature you can even do things like

main :: IO ()
main = print $ coerceInteger expr : expr : expr : expr : expr : expr
    where
        expr :: Expr a => a
        expr = literal 100

without any problems, this will print out `[100, 100, 100, 100, 100, 100]`. The initial `coerceInteger` is needed though, because otherwise the compiler won't know what to instantiate it as and therefore won't have a `Show` instance for `print`.

Problem

I'm currently learning Haskell and trying to understand how typeclasses are evaluated, and how `let` and `where` work. This code runs fine: ``` {-# LANGUAGE FlexibleInstances #-} class Expr a where literal :: Integer -> a instance Expr Integer where literal = id instance Expr [Integer] where literal i = [i] coerceInteger :: Integer -> Integer coerceInteger = id main = print $ coerceInteger (literal 100) : literal 100 -- Prints [100,100] ``` but changing the main function to ``` main = print $ coerceInteger expr : expr where expr = literal 200 ``` causes a compiler error: ``` Couldn't match expected type `[Integer]' with actual type `Integer' In the second argument of `(:)', namely `expr' In the second argument of `($)', namely `coerceInteger expr : expr' In the expression: print $ coerceInteger expr : expr ``` I'm guessing this is because in the first `main` method the `literal 100` is evaluated twice, whereas in the second example `literal 200` is only evaluated once and so the compiler is forced to choose a type. How can I factor out that code to avoid repeating myself, without causing this error? I tried using `let expr = literal 300 in ...` but ran into the same issue.

Original source