4 ms·
Exceptions are monadic
- pufuwozu 15y agoAuthor here: thanks for submitting this!
- deleted 15y ago[deleted]
- T-R 15y agoIf you were to implement this in a procedural language with side effects, what would you do about "finally"?
- rix0r 15y agoIt would be a little more cumbersome to write, but the same effect can be achieved easily: 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.
- arethuza 15y ago"even more cleanly" I think I prefer the try..catch..finally version - looks much cleaner to me.
- rix0r 15y agoWell yeah, but I thought the point was they were equivalent: i.e., one is simply syntactic sugar for the other.
- virmundi 15y agoIt doesn't seem like the non-finally version will necessarily run in every case. In the instance of try/finally, a runtime exception might occur as well as a checked exception. finally will always run. But a.cleaup will not.
- T-R 15y agoForgive my ignorance, but what about this method saves us from having to use continuations to unwind/rewind the stack, as Exceptions do?
- batterseapower 15y agoYou can implement "finally" as a higher order function in Haskell: 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)"
- _delirium 15y agoThis is kind of interesting for collapsing two solutions to the error-signaling problem that are usually seen as alternatives. Or at least, I think of them as alternatives, and they're often presented that way. 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.
- rian 15y agothe simple use of "Either" does not instantly qualify something as "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. 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 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
- gmartres 15y agoSo, what's the intuitive equivalent to monadic parsers combinators? (see http://www.cs.nott.ac.uk/~gmh/monparsing.ps http://www.cs.nott.ac.uk/~gmh/monparsing.ps which incidentally is a great introduction to monads). Similarly, now that I have some understanding of monads, I find myself wishing I could use them(particularly the State monad) in other languages to reduce the boilerplate.
- rian 15y agosigh, http://boost-spirit.com/home/ http://boost-spirit.com/home/
- Peaker 15y agoThis is an example from this parsing framework: 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
- virmundi 15y agoHow does this work for constructors? I have the habit of guarding against poor input. If a value is null, I'll throw a null pointer exception or an illegal argument exception. While I recognize that both of these unchecked exceptions, I could just as easily throw a ModelArgumentException that extends from Exception. 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.
- tsewlliw 15y ago> How does this work for constructors? There's always static factory methods, for when constructors just can't do what you need.
- virmundi 15y agoBut doesn't this basically mean that my constructor has to allow for invalid states? Also, doesn't that mean that another object has the rules for what makes the first object valid? Isn't that a violation of separation of concerns?
- tsewlliw 15y agoI'm coming at this with Java semantics, apologies if that just doesn't work for you :) I'd make the constructor private, and move the validation into a public static factory method on the same class that returns an Either.
- T-R 15y agoPerhaps you could return an Either from the constructor, and then either have a special construct to handle Left: 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).