Since Haskell is lazily evaluated, why doesn't this code work?

  • A+
--defining function safeHead :: [a] -> Maybe a safeHead [] = Nothing safeHead (x:_) = Just x  --calling function safeHead (4:5:3:[]:[]) 

When I call safeHead with the argument (4:5:3:[]:[]) the pattern only evaluates the argument to see if either it is empty or if it has a head. So despite the nonsensical bit "[]:[]" it should not throw an error since this part is not even evaluated.


it should not throw an error since this part is not even evaluated.

The fact that it is not evaluated is irrelevant. Haskell is a statically typed language, and the compiler checks the types. It does not have to calculate the result in order to typecheck the value: the input and output type of all functions are known (well calculated), and the Haskell compiler verifies that type of the output of one function is the same as the input of the function that is called to process that output.

The typechecking is done at compile time, and the Haskell compiler is not lazy (in the sense that it performs these checks before generating the binary, not just before running the code). The compiler eagerly checks the types and guarantees that the program is sensical from a type system perspective.

For example the following expression will typecheck:

1 : 2 : undefined 

but undefined :: a will raise an error in case it is evaluated.

Haskell allows to define new types of Numbers, so you could make a typeclass that parses the numbers 4, 5 and 3 into numbers of your own type. That type could, strictly speaking be a list.

But Haskell will, if you evaluate this, not find a type to use, and will raise an error:

Prelude> safeHead (4:5:3:[]:[])  <interactive>:6:1: error:     • Non type-variable argument in the constraint: Num [t]       (Use FlexibleContexts to permit this)     • When checking the inferred type         it :: forall t. Num [t] => Maybe [t] 

So here it is looking for a type for the elements of the list, and it finds out that these should be lists, due to the [] element (the one but last element), but at the same time these should be Nums, now it fails to find such a type, hence the error.

We can strictly speaking construct such a type, such that we can use the function in a proper way:

Prelude> data A = A deriving Show Prelude> :{ Prelude| instance Num [A] where Prelude|     (+) = undefined Prelude|     (*) = undefined Prelude|     abs = undefined Prelude|     fromInteger _ = [A] Prelude|     negate = undefined Prelude|     signum = undefined Prelude| :} Prelude> :{ Prelude| safeHead :: [a] -> Maybe a Prelude| safeHead [] = Nothing Prelude| safeHead (x:_) = Just x Prelude| :} Prelude> safeHead (4:5:3:[]:[]) :: Maybe [A] Just [A] 


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: