Appreciation, let and where in Haskell

I am currently studying Haskell and trying to understand how models are evaluated and how let and where . This code works 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 assume that this is because in the first method, the main literal 100 is evaluated twice, while in the second example, the literal 200 is evaluated only once, so the compiler is forced to choose the type.

How can I adjust this code to avoid repeating myself without causing this error? I tried using let expr = literal 300 in ... but ran into the same problem.

+6
source share
1 answer

The problem is that literal 200 interpreted differently in 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 on types, the compiler determines that the first literal 100 must be of type Integer , because it is passed to coerceInteger , since it must be of type Integer . This also sets the type (:) now Integer -> [Integer] -> [Integer] , implying that the last literal 100 must be of type [Integer] .

In the second example, you say 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 check the type.

This is actually due to the terrible restriction of monomorphism. You can fix this problem in two ways: one, disable monomorphism restriction using {-# LANGUAGE NoMonomorphismRestriction #-} , or you can provide an explicit expr type that will generalize it:

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

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


In fact, once you add a 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 

no problem, it will print [100, 100, 100, 100, 100, 100] . The initial coerceInteger needed because otherwise the compiler will not know what to create, and therefore will not have a Show instance for print .

+9
source

Source: https://habr.com/ru/post/972214/


All Articles