| > | mcompare x y =
| > | do i <- x ==~ y
| > | if i then return EQ else do
| > | i <- x <=~ y
| > | if i then return LT else do
| > | return GT
|
| The reason you didn't understand it the first time is because you have
| never seen that idiom (using Monad return to simulate C-style return)
| before. Once you understood the idiom, you were able to understand it.
| For people like me who understand the idiom well, the code is quite
| readable.
No, it's more than that. I actually understand the C idiom very well
and occasionally use it myself (in C). The problem is that, knowing
that C-style is being simulated in Haskell, I have to be extra careful
to double-check the code for slip-ups.
Several people have already pointed out that my example was
syntactically flawed. Maybe such slip-ups are not as easy as I
imagined, but let's have another attempt:
mcompare x y =
do i <- x ==~ y
if i then return EQ else do debug "not equal"
i <- x <=~ y
if i then return LT else do return GT
The bug is that this version will return GT where EQ was wanted.
However, I appreciate that sometimes people do want C-style control flow:
> possiblyAct :: IO ()
> possiblyAct = do f <- checkFlag
> if not f then return ()
> else do {- stuff -}
>
> ... conceptually, {-stuff-} is in the same path of control as
> `f <- checkFlag'.
but I have to disagree with the quoted comment - `stuff' is not in the
same control block at all: it is in a nested block guarded by a
condition. It is a conceptual error to think of this as straight-line
code with a `goto' jumping out part-way through. However much you
might want this Haskell source code to look like assembler, it's not
what the semantics of the language give you, and it's not what the
compiler is going to understand by your code, because the layout rules
(even if modified to fit this style) are still going to insert the
nesting {}s.
Simon says:
> I've been working on the assumption that to a first approximation you
> can just look for the last line indented *less* than the current
> declaration to find the start of the current binding group.
> [...]
> However, this view isn't held by everyone - another popular view
> is Malcolm's:
Actually, I tend to agree with Simon here. In my previous example:
> class M a where
> m :: a
> m = ...
I argued that the programmer probably intends a non-empty class decl.,
and I still think so, but I also agree that:
> I don't think this is particularly useful, because the example class
> declaration would encompass the entire rest of the module.
Like I said before, the problem here is actually that you can have an
empty where clause. My position is that this situation should be parsed
according to the strictly-increasing indentation rule, but flagged with
a warning. People who like writing empty where clauses can still do so,
but everyone else would get help to find their unintentional layout bugs.
Also, Simon also earlier claimed that "'do' expressions cannot be empty".
This is incorrect - according to the Report,
exp10 -> ...
| do { stmts }
stmts -> stmt1 ; ... ; stmtn (n>=0)
stmt -> ...
| (empty)
so your proposed change to ghc:
> Hence I've modified GHC to allow do-expressions to nest at
> the same indentation level - strictly an extension to Haskell 98, but it
> doesn't break any legal Haskell 98 programs
does in fact break legal Haskell 98 programs.
Now this idea of allowing empty lists of declarations/stmts etc., is
beginning to bother me a little. It was new in Haskell'98, and
initially seemed harmless enough. Can someone remind me of the
rationale?
Regards,
Malcolm