I believe you. But my experience is still that my code is a lot shorter, less indented, and easier to read in languages like OCaml or F#.
Depending on what you're writing, a lot shorter, I'll give you that.
I wrote solutions of a couple of assignments in Caml, and decided to write them both in functionnal and iterative styles, the second ones were indeed longer and with more complex indentation.
Huh? Why not let rec . . . and ?
Because you can't use a function call in the right hand of a let rec to statically prevent recursive definitions that could trigger a bus error.
But in fact, it's even harder in my situation, but I can't fault OCaml. I have to use Caml Light, and it forbids even a constant declaration in the right hand side of a let rec, like in:
Code:
let rec a = 2 and f = function 0 -> 1 | x -> a* (f (x-1)) in f 5;;
(correct in OCaml, but forbidden in Caml Light)
But look, I can't discount your personal experience. I'm sorry you've had such headaches with it.
Well, that's not THAT bad... And I probably wouldn't think so much about it if I was only writing code for myself (or experienced programmers).
The issue is that I *teach* it. So I keep wondering what will be the best for students. And I find difficult to abide to a set of rules.
The students are already doing such awful and unreadable things that I have to do my best to keep their heads as tidy as I can ^_^ (honestly, I think reading beginners code is ten times harder in Caml than in Python, C, Ada, Pascal, assembly (!) or even Java)
Let's take a basic example, dichotomy to find the index of an element in a vect:
Code:
let Index elem v =
let rec Aux = fun
| a b when a>b -> failwith "Not found"
| a b -> let c = (a+b)/2 in
if v.(c) = elem then c else
if v.(c) > elem then (Aux a (c-1)) else
(Aux (c+1) b)
in Aux 0 (vect_length v - 1);;
For the in placement, I usually stick to the rule "if I can put it at the end of the let... line, I'll place it there, or else, I'll put it at the beginning of a line, aligned with the matching let". Usually, it works (like above), although there's a couple situation where it's becoming a bit akward (for example, when the in at the beginning of the line is followed by a let or a match, I don't know how to indent the following lines)
In the above example, though:
- I don't know where I should place the "if"s. I'm not that fond of putting them far to the left of let, and putting it 2/4 spaces to the right is unusable.
- in the case of multiple branches tests, guidelines suggest to align the "if"s. Suits me (although whether there's a "else" at the end of the previous line or not makes things hazardous) but I'm not that fond of aligning the last consequence with the ifs.
Definitively no big deal, but on too many occasions I wonder what would be the best solution.
And yes, I know I could write, in this case, for example
Code:
let Index elem v =
let rec Aux a b = let c=(a+b)/2 in match a, b, c with
| a, b, _ when a > b -> failwith "Not found"
| _, _, c when v.(c) = elem -> c
| a, _, c when v.(c) > elem -> Aux a (c-1)
| _, b, c -> Aux (c+1) b
in Aux 0 (vect_length v - 1);;
which solve neatly most of the issues above (except maybe where you should put the in match)... I just don't have trickier cases on hand currently. Besides, it's slightly more difficult to write the second solution than the first, starting from the algorithm itself, for beginners.