Exceptions are monadic
blogs.atlassian.com
blogs.atlassian.com
The classic error-signaling approach is in-band signaling through 'error' return values, but that has the problem of any in-band-signaling solution, clash between the control and data values. Two possible solutions are: 1) in-band signaling with a more complex (data,control) encoding scheme, in this case every function returning (value,error-code); or 2) out-of-band signaling. The simplest example of #2 is C's errno facility, with one global out-of-band error status system; exceptions can be taken as a more sophisticated improvement that implements local context, along with infrastructure to make it easy to specify error-handling policies.
But it's interesting to think of exceptions as syntactic sugar on an in-band-signaling solution, rather than out-of-band signaling at all (at least conceptually), with Monads wrapping the code to check/propagate the error signal.
The reason I throw such exceptions is that I don't want my object to be constructed with invalid state (this is especially true when my object is immutable, but has actions). Since I cannot return an Either, I believe that I must either A) throw an exception or B) allow invalid state. I don't like either solution.
I'd make the constructor private, and move the validation into a public static factory method on the same class that returns an Either.
myObj = new Object() :{ cleanup() }
or require dummy objects in case of failure: myObj = new Object() ?? new DummyObject()
Admittedly less than optimal. The more I think about it, the more I think it's a bad idea... The first still means that you need a separate catch block for each statement. The second is essentially swallowing the exception.Aside from that, using Either in the return kills your ability to do method chaining, unless you build method chaining into an operator and have it propagate the Either (similar to bind).
AFAIR even in scala you either use a factory method in a companion object or use "real" constructors.
The common idiom for implementing constructors in subclasses is to do "self = [super init]; if (self) { ... subclass constructor impl ... }; return self".
Setting self to nil is a common use-case.
A a = new A();
try {
B(a);
return C(a);
} finally {
a.cleanup();
}
Transforms into: Either<Exception, Result> ret;
A a = new A();
ret = B(a);
if (ret.isRight()) ret = C(a);
a.cleanup();
return ret;
Maybe someone can rewrite this even more cleanly. I can think of an easy-to-generate improvement containing a GOTO.I think I prefer the try..catch..finally version - looks much cleaner to me.
finally :: Either err a -> IO () -> (a -> IO (Either err b)) -> IO (Either err b)
finally (Left err) cleanup _k = cleanup >> return (Left err)
finally (Right x) _cleanup k = k x
You avoid having to unwind the stack because the user of finally has to manually convert into CPS in order to supply the higher order argument of type "a -> IO (Either err b)"you know what else is monadic? NULL pointers. oh wait, you know what else is monadic? unix pipes. you know what else is monadic? CPP. really old shit.
just because the word monad isn't everyday english doesn't make it a cool or even an interesting concept. it's just an awkward abstraction for something everyone already intuitively understands. monads were invented for the sole purpose of giving haskell a programmable model for IO, that's it. it's just a concept that's only useful for haskell programming, everything else already has an intuitive programming model for IO.
it's like building up integers to be this crazy complicated concept and then saying Exceptions are integers. awesome, good job, you've just found an equivalence, here's a pat on the back (psst http://en.wikipedia.org/wiki/Church_encoding). i guess i should expect these kinds of vacuous observations given all the BufferedManagerFactorySingleton crap code java slaves write.
you know what else is monadic? state machines. you know what else is monadic? imperative programming. wait wait wait! you know what else is monadic? JAVA ITSELF head explodes
text = lexeme[+(char_ - '<') [_val += _1]];
node = (xml | text) [_val = _1];
start_tag =
'<'
>> !lit('/')
>> lexeme[+(char_ - '>') [_val += _1]]
>> '>'
;
end_tag =
"</"
>> string(_r1)
>> '>'
;
xml =
start_tag [at_c<0>(_val) = _1]
>> *node [push_back(at_c<1>(_val), _1)]
>> end_tag(at_c<0>(_val))
;
The (>>) operator here is defined in the same as the Applicative/Monad bind: (>>).
For the effect of full monadic bind (>>=), the explicit assignment into "val" is used.Here is the same example encoded using Monadic Parsec:
text = many (notChar '<')
node = xml <|> text
start_tag = do
char '<'
notChar '/'
val <- many (notChar '>')
char '>'
return val
end_tag name = do
string "</"
string name
char ">"
xml = do
name <- start_tag
val <- node
end_tag name
return (name, val)
Now, one interesting difference between Parsec and the spirit framework, is that Parsec instantiated the Monad class to allow composition of Parsers, whereas spirit defines its own ad-hoc combinators.This means that with parsec, I can use all of the existing monadic functions.
For example, I can use the "replicateM" function, which replicates a monadic action N times to say I want 3 XML elements in sequence:
replicateM 3 xml
Or I can use one of the hundreds of others of available combinators.Of course boost spirit can also encode these combinators on top of their own ad-hoc operators, but they would be:
* Duplicate code with every other monadic framework
* Another vocabulary to learn in order to compose parsers
* Cannot re-use any third party code that works with every monad in existence
> the simple use of "Either" does not instantly qualify something as "monadic"
The composition of calls to functions that return Either as to implement exceptions is exactly Monadic.
> you know what else is monadic? NULL pointers. oh wait, you know what else is monadic? unix pipes. you know what else is monadic? CPP. really old shit.
Only if very loosely interpreted. NULL pointers are not monadic. Maybe types are monadic (and also considered an "Exception monad").
Unix pipes are not monadic in a meaningful way (they don't support the monadic operations), but approximations/implementations of them can be.
In what sense is the C preprocessor monadic?
> just because the word monad isn't everyday english doesn't make it a cool or even an interesting concept. it's just an awkward abstraction for something everyone already intuitively understands.
Cool and interesting are subjective concepts. I find them pretty cool, in the sense of capturing one of the most general patterns in code I've ever seen captured.
In what sense are monads an "awkward" abstraction?
> monads were invented for the sole purpose of giving haskell a programmable model for IO, that's it. it's just a concept that's only useful for haskell programming, everything else already has an intuitive programming model for IO.
That's completely false. Monads don't have anything to do with IO, it's just a particular instance of them. I can make a list of Bananas, does a list have anything to do with bananas?
Monads are immensely useful for many things. When using Haskell, I can use a very large set of combinators that work with any monad, to compose Parsers, functions, Maybe values, and various other types.
This means I can re-use a lot more code and vocabulary. When I learn an API, I don't have to read the documentation and types of 30 specific combinators. Instead, the library just says: "This type has a monad instance" -- and I can use my existing knowledge to work with it.
> it's like building up integers to be this crazy complicated concept and then saying Exceptions are integers. awesome, good job, you've just found an equivalence, here's a pat on the back
No -- the semantics of statement composition in the face of exceptions is the only valid instance of Monad for Either values that encode them. It's not like you have to go very far to find this equivalence. Integers are not usefully equivalent to exceptions.
> i guess i should expect these kinds of vacuous observations given all the BufferedManagerFactorySingleton crap code java slaves write.
This observation is actually one of the first uses of monads in the Haskell crowd, not in the Java crowd.
> you know what else is monadic? state machines. you know what else is monadic? imperative programming. wait wait wait! you know what else is monadic? JAVA ITSELF head explodes
I think you've completely missed the point of Monads.
Take a look at:
http://haskell.org/ghc/docs/latest/html/libraries/base/Contr...
I can use any of the combinators in there with any monad instance. The fact I can use them with Either values makes Either value composition behave exactly like exceptions.
This is not a "neat observation" -- it is something people routinely use, plentifully.
unix pipes as monads: http://okmij.org/ftp/Computation/monadic-shell.html
cpp as a monad: http://conal.net/blog/posts/the-c-language-is-purely-functio...
monads invented for IO / functional impurity: http://homepages.inf.ed.ac.uk/wadler/papers/monads/monads.ps...
"This is not a "neat observation" -- it is something people routinely use, plentifully." ...in haskell
and who are you to say that integers are not usefully equivalent to exceptions??? have you ever heard of errno? i don't have exceptions or monads in bf, just pointers. i need an equivalence so that i can feel smarter when i code bf. and if you dare say that bf isn't a useful programming language then you better stop coding in java...
Unix pipes as monads is trying to create an analogy (an imprecise one) between unix pipe operations and monadic operations. The monadic laws do not hold with unix pipes, so the analogy is pretty weak.
You might want to re-read Conal's "the c language is purely functional" because it makes no claim about cpp being a monad, at all.
Let's look at the monad instances/examples listed by the Wadler paper, in order:
* The identity monad
* The strictness monad
* The state monad
* Filters (MonadZero)
* Monad Morphisms
Under "More Monads":
* Parsers
* Exceptions
* Input and Output
Are you trying to use this paper to establish that monads were invented exclusively for I/O and now perverted to describe exceptions? Note how exceptions are listed as an example use of monads before I/O.
> and who are you to say that integers are not usefully equivalent to exceptions??? have you ever heard of errno?
errno is not an exception mechanism. You have to indicate, in-band, that there's an error result, then check for that result, and then use errno as well, to implement exceptions.
Whereas the Either monad itself implements both the in-band encoding of errors, and the caller's error-check and propagation.
Applicative and Unix pipes cannot change their computation topology.
>monads were invented for the sole purpose of giving haskell a programmable model for IO, that's it.
Monads were known long before Haskell. They just used in Haskell for IO and for much more things.
For example, group and ring theory often starts with the concept of symmetries, but it can then be shown, through an equivalence (or in mathematical terms, an isomorphism) that these have a relationship to polynomials. From that, we then easily show that there is no general formula for polynomials of degree greater than 5. Two vastly different fields, actually sharing something, and having a very useful result. Similarly for finite state machines and monoids.
If you can express something as something else, you have the opportunity to shed light on it's structure and hence learn something new about it. Yes lots of things share that structure, it just so happens that monads are quite a general structure, that doesn't mean that no interesting results are attached to them.