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.
Referential transparency means you can take an expression in your program, and replace it with the result of that expression. As an example, if you have the expression 5+4, you can replace that with 9. That's what it means. Now 5+4 is easy because you can do that in your head without running the program.
Some functional programming languages enforce referential transparency for all functions. The importance of referential transparency is that it allows the programmer and the compiler to reason about program behavior as a rewrite system.
A function is called referential transparent if it always return the same result value when called with the same argument value. For referential transparency, we need our function to be pure and immutable. Example : The method `String.
As we all know, it is good practice to avoid functions with side effects. This leaves imperative programmers with pure functions and pure effects. Referential transparency is then a powerful tool for imperative programmers to make their programs easier to reason about, and easier to test.
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 Int
s (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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With