Haskell length + map description?

I play with Haskell since I am learning a language and I just found something I don’t understand and I can’t find an explanation. If I try to run this code:

map (`div` 0) [1,2,3,4] 

I get a divide exception of 0, which is expected. But if I ran this code:

 length (map (`div` 0) [1,2,3,4]) 

I get 4!

I would like to know why I do not get a divide by 0 exception when I do a mapping inside a length function!

+7
haskell
source share
3 answers

The map and length functions can be defined as follows:

 map :: (a -> b) -> [a] -> [b] map _ [] = [] map f (x:xs) = fx : map f xs length :: [a] -> Int length [] = 0 length (_:xs) = 1 + length xs 

Now let's find out why your second example works the way it does. This happens as follows:

 length (map (`div` 0) (1:2:3:4:[])) = length (1 `div` 0 : map (`div` 0) (2:3:4:[])) -- second map equation = 1 + (length (map (`div` 0) (2:3:4:[]))) -- second length equation = 1 + (length (2 `div` 0 : map (`div` 0) (3:4:[]))) -- second map equation . . . = 1 + (1 + (1 + (1 + length (map (`div` 0) []))))) -- second length equation = 1 + (1 + (1 + (1 + length [])))) -- first map equation = 1 + (1 + (1 + (1 + 0)))) -- first length equation = 4 -- arithmetic 

What is the trick here? In Haskell, the process of evaluating an expression is called forced. Forcing an expression does the minimal work necessary to define an external result data constructor. As part of this, subexpressions will be forced only as necessary to achieve the goal.

So, in this example, the outermost expression that we force is the use of the length function. The definition of length has two cases: one that uses the list constructor [] , and the other that uses the constructor (:) , so to apply length we need to figure out which of these two cases applies to the argument. Since the argument has no constructor in its most extreme position, we are forced to figure it out. What happens in the step between the first and second lines of output above; we force the map subexpression by looking at its arguments and choosing the second map equation.

But after this point, we have all the information necessary to determine which of the two equations for length is applied, so we go by the "extreme first" rule and apply the corresponding length equation. In this case, it discards the subexpression that contains the division by zero, which means that the subexpression will never be forced, and the error will never be triggered.

In the first example, however, when entering an expression in GHCI, you implicitly ask the interpreter to print its result. This requires him to make the list spine access its elements and make the elements themselves print them. Thus, division by zero error occurs when you force the first element of the list.


EDIT: Let me point out one nuance that you might not have noticed. When we try your first example in GHCI, this is the result of the session:

 *Main> map (`div` 0) [1,2,3,4] [*** Exception: divide by zero 

Look at this lone square bracket at the beginning of the second line? This is the opening bracket for the list that was printed before dividing by zero error. Also note what happens in this example:

 *Main> map (20 `div`) [1,2,0,4] [20,10,*** Exception: divide by zero 

The first two elements of the list of results, and even the comma separating the second element from the third, are successfully printed because Haskell does not try to calculate the third element of the list until it is printed.

+14
source share

If you enter the map expression in the interpreter, it will evaluate it, and then print the resulting list. To do this, all the elements of the resulting list must be evaluated, because they will be part of the displayed string.

However, when the interpreter evaluates the length expression, it only needs to look at the structure of the resulting list. He should not look at the actual elements inside the list. Since Haskell, being a lazy language, evaluates only what it needs, it means that the elements will not be evaluated and thus no exception will be thrown.

+5
source share

This is a good old Haskell lazy rating! If Haskell does not have to calculate something, it will not. In this case, you call map in a list of length 4. As for Haskell, applying map to any list returns a list of the same size, no matter what operation you use. Therefore, Haskell just tells you that the length is 4, not dividing by 0.

+5
source share

All Articles