|
|
In C and C++, assignment is an operator. Which, obviously, is pretty weird.
However, Haskell can go one better than that. ;-)
I was just reading "Thinking in C++", and it started talking about how
the assignment operator works. In particular, "x = 4" is a valid
assignment, but "4 = x" is not. "Because, obviously, that would be
pretty silly."
But in Haskell... actually *both* of those are perfectly legal. o_O
Consider, for example, the function
foo x =
let 4 = x
in 2*x
Since the let-binding isn't "used" for anything, it actually has no
effect (due to lazy evaluation). For a similar reason, if I transform
this into a monadic function:
foo x = do
let 4 = x
return (2*x)
then still it does nothing. (Again, it isn't "used" - nor is it possible
to "use" it.)
However, if I insert this into the monadic binding sequence, thus
forcing it to execute, something does happen:
foo x = do
4 <- return x
return (2*x)
Exactly what happens here depends on which monad you use. (This function
can be run in *any* monad.) If you run it in the IO monad, the following
happens:
- "foo 4" will return the number "8".
- "foo 1" will throw an exception with a rather cryptic message.
If instead you run this inside the list monad, something slightly
different occurs:
- "foo 4" will return a 1-element list that contains "8".
- "foo 1" will return an empty list.
So it seems that essentially we have just created a rather obfuscated
way to check wether x = 4 or not! :-D
All of this works because in Haskell, the LHS of a definition is *not*
required to be a variable - it is a *pattern*, and the definition causes
the value produced by the RHS to be *pattern matched* against the LHS. I
variable is a valid pattern, but so is "4".
To illustrate why the language is designed this way, consider
let [x,y,z] = [1,2,3]
It should be relatively obvious what this does. (It says x=1, y=2 and
z=3.) While this example is pretty pointless, it is however quite common
to want to do something like
let (list1, list2) = splitAt 5 my_list
where a function returns a pair of results and you want to assign each
one to a different variable.
The reason the final version of foo does what it does is due to a subtle
edge-case of the language. Firstly, pattern matching can fail. If you
say "x = 3", this cannot possibly fail. However, if you say "[x,y,z] =
[1,2]", the pattern match fails since the lists aren't the same size.
In a let-expression, a pattern match failure causes an exception to be
thrown. However, in a monadic let-statement, it causes the "fail" method
of the monad to be called. By default this also throws an exception, but
it depends on the monad; it can be redefined to do something else. And
that's exactly what the list monad does - it redefines "fail" to just
return an empty list!
So there we are - wasn't that pointless? :-D
--
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*
Post a reply to this message
|
|