Hi Evgeny,
I'll add a few more parentheses to clear up exactly what's going on (all according to the precedences of OCaml in
this table). First, let's consider your penultimate example.
(return 1) >>| (fun r -> (2 >>| (fun r' -> (r, r'))));;
If you look at the part closer to the end, you find this subexpression:
2 >>| (fun r' -> (r, r'))
The typecheck error in the penultimate expression was that `2` is not a `('a, 'e) result`. We can fix this by changing `2` to `return 2`, but the evaluation is not what was originally intended:
utop # (return 1) >>| (fun r -> ((return 2) >>| (fun r' -> (r, r'))));;
- : ((int * int, 'a) result, 'b) result = Ok (Ok (1, 2))
Now the evaluates to a nested result. This is because each map (`>>|`) will wrap the returned expression. If you expanded this to (1, 2, 3, 4, 5) using all maps, you would ended up with a five nested result - one for each map. Since you do not want the additional nesting, you can convert all but the ultimate map (`>>|`) into a bind (`>>=`). This will fix the nested problem and results in this expression:
utop # (return 1) >>= (fun r -> ((return 2) >>| (fun r' -> (r, r'))));;
This fixed up expression is your ultimate expression!
In case the more technical explanation is hard to follow (which I expect it to be), here is a different way to approach constructing the final expression. Your first expression (the single return) is straightforward. Trying to build the second expression right after, you insert `fun r -> 2 >>|` in the middle of first expression. It may be clearer to instead not break up the first expression and instead prepend `return 2 >>= fun r' -> ` which is basically your third expression (with a few numbers/names swapped).
Hopefully that clears it up! If anything is still not clear, please let me know, I'm more than happy to try to explain anything in a different way (especially since I don't think this explanation is particularly clear).
--
Zander!