Referential transparency and guard

Viewed 64

From Learn You a Haskell for great good! I got:

ghci> [1..50] >>= (\x -> guard ('7' `elem` show x) >> return x)
[7,17,27,37,47]

I changed the example to:

ghci> [1..50] >>= (\x -> if x > 25 then [] else [()] >> return x)
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]

So far so good. But now I want the other 25 numbers:

ghci> [1..50] >>= (\x -> if x > 25 then [()] else [] >> return x)

<interactive>:204:2: error:
    • No instance for (Num ()) arising from the literal ‘1’
    • In the expression: 1
      In the first argument of ‘(>>=)’, namely ‘[1 .. 50]’
      In the expression:
        [1 .. 50] >>= (\ x -> if x > 25 then [()] else [] >> return x)

I can work around it like so:

ghci> [1..50] >>= (\x -> if x < 26 then [] else [()] >> return x)
[26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50]

My question is the following: I thought that because of referential transparancy I could replace the guard with an if ... then ... else clause. By proof of the above examples, it seems to work except for the case when "[()]" is first in the clause. Why is this? I try to get a deeper understanding of what is going on here. I am interested in the mechanics of Haskell. This feels inconsistent somehow. I am very interested in your answers.

1 Answers

The reason this fails is because now the compiler reads this as:

(\x -> if x > 25 then [()] else ([] >> return x))

This is important since the else part will return a list of Ints (so [Int]) here, whereas the then part will return a list of a "unit" type () (so [()]) and of course these two do not match. Since an if-then-else should return values of the same type on both the then and the else clause, it thus says that unless () is a Num type (such that it can parse 1 and 50, etc. to that type), we have a problem.

so the >> return x is part of the else … clause. By adding brackets, you can obtain the same result:

(\x -> (if x > 25 then [()] else []) >> return x)

For example:

Prelude> [1..50] >>= (\x -> (if x > 25 then [] else [()]) >> return x)
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]
Prelude> [1..50] >>= (\x -> (if x > 25 then [()] else []) >> return x)
[26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50]
Related