I don't think the monadic model is any different, it's a generalisation that applies to either perspective. Because we can actually have the same debate over async functions: some people like to think about a function that returns Future<f64> and it would seem like a type error to say an async function returns 42.0, other people see it as a function that returns 42.0 but has some extra "async-ness" (which is the same kind of thing as being a fallible function). And if you think about it that's, in a certain sense, the same disagreement (and we can and do have the same debate in the context of a generic monad).
Though both Result and Future types can be described as a monadic shape, it only works if you fix the Error type. Even then, you've lost the value in the Result entirely. The interesting and useful thing about a Result is the part of it that breaks from the monadic mold, the error case.
Nonsense. Yes Results can only be composed monadically if they have the same error type, but that's true for any other way of composing Results too - if you want to use Rust's ? operator then you need to make sure all the results you're going to use it on share (or are at least are convertible to) the same error type.
Of course for a specific monad to be useful you eventually have to do something specific with it, but again that's completely normal. The point of using the monad abstraction is not to forget that there is a difference between Result and Future, it's to be able to write the common part in a common way - just as, even though you might use a generic List implementation to store lists of integers and lists of strings, you will eventually want to do something with the contents of the list that is specific to either integers or strings.
The error needs to be a monoid to be able to flatten the Result, if it is not, it will break, you will lose information. E.g. if you have:
R<E1,R<E2,A>> -- flatten --> R<E1 + E2, A>
R stands for result, E1,E2 are errors.
You need to think up a reasonable implementation of +. This is of course easily solvable, you can just add them together. Keep a list of errors in your result instead of one error and you are done. Just keep the +, it is free :p
This is completely wrong. Result is either success or failure, so an R<E1, R<E2, A>> can never contain both E1 and E2 at the same time.
Validation types work the way you describe and require a monoidal "error" side, but famously do not form monads (though they are applicative functors).
While compelling, flattening doesn't make something a monad, does it? Not snark, if that's a consequence of one of the three laws, I'm just unaware of it. I know what monads are but I'm not practised with them.
But of left identity, right identity, and associativity, what is violated?
So, I was a bit wrong with the monoid, I meant to add them on typelevel by using a sumtype. Sorry, sometimes bit confused after work.
No flattening doesn't make something a monad. Return and flattening are the interesting operations of a monad, that together with the laws makes the monad.
>But of left identity, right identity, and associativity, what is violated?
Well, there is not something violated perse, but you cannot write a flatten operation for something like this:
R<R<u32, E1>, E2>
However you could write it for this:
R<R<u32, E1 + E2>, E1 + E2>
(That was what I should have written above btw) Because now the error types are the same.
I've found this kind of pattern much nicer in an application context. Rust sort of has this going on with anyhow[1] for applications and thiserror[2] for libraries (or just writing your own Error type).
The number of variables doesn't matter, as long as they are the same type is the part that matters.
<T, E> can be thought of as a pair, tuple, cartesian product. Likewise you can expand it to as many values as you want.
If type C is just (T, E) then it is really just Result<C> at that point.
The first T can vary but as long as everything after that is fixed to the same type, you can have as many variables/types as you want and it will still be monadic.
You’re reducing two type variables down to one. The number of type variables matter. The kind of the type matters. Look at the definition of Monad for Either. You’ll see.
For any E, Result<E, ?> forms a valid monad. So yes technically Result is a family of monads rather than a single monad, just as technically the monad is not the type itself but the combination of the type and two functions defined on that type, but that's irrelevant pedantry most of the time.