4 ms·
I naturally tend to use this "railway" approach in my code. But often I stumble upon various problems: 1. The "success, but..." scenario. Sometimes you have di
by gioele 12y ago
I naturally tend to use this "railway" approach in my code. But often I stumble upon various problems:
1. The "success, but..." scenario. Sometimes you have different types of failures to handle and this style conflates all of them into "failure". For example, I want to WARN about something my caller; everything is OK, but there is something to be known (The call went fine, but the file was not there so I created it). Should information about that value be passed on the failure pipe? If I do so I lose the ability to do simple checks like "if failure.empty?". A third pipe for "warn"? And another one for "info"? Mhhh...
2. There are different way to fail. There are cases in which you really should not go on with any computation, there are cases in which can go on, just note that you are in "danger" mode (ENOSPC).
3. What about transient failures? Should "Network just went down" be treated like "Server returned HTTP 404".
4. Sometimes you want to just ignore all the errors because you are prototyping something and you do not want a lot of boilerplate code laying around. And definitely you do not want a catch all that hides the errors.
- anon4 12y agoI think you're supposed to split your logic in enough tiny functions that each really only has success or fail outcomes. I haven't tried it though.
- alexatkeplar 12y agoTotally agree with you re 1. The "success, but..." scenario. We're trying to figure it out at the moment in Snowplow. We process raw events and either accumulate errors on Failure or compose an enriched event on Success. The trouble is, we would like to switch some of these errors to warnings, so we would need to push both the warnings and the enriched event through on Success. I think we need to switch: Validation[NonEmptyList[ProcessingMessage], EnrichedEvent] to something like: Validation[NonEmptyList[ProcessingMessage], Tuple2[List[ProcessingMessage], EnrichedEvent]] It feels a bit clunky though...
- jerf 12y ago"1. The "success, but..." scenario. Sometimes you have different types of failures to handle and this style conflates all of them into "failure"." No, it doesn't. I'm not sure where you get that, because even in this example "Failure" carries an error message. That's a string for didactic purposes, it can be anything you need it to be. "2. There are different way to fail. There are cases in which you really should not go on with any computation, there are cases in which can go on, just note that you are in "danger" mode (ENOSPC)." Here is where avoiding the word "monad" probably does not play to the author's favor. Monadic computations are able to examine the results of computations, so nothing stops you from case somethingThatMayFail arg1 arg2 of Right x -> do ... -- carry on the computation Left x -> -- fail out however you're going to fail Which I believe also handles the rest of your questions... the pattern conveniently propagates errors when you don't want to treat them specially, but you can still dig in and examine them if you need to. Whatever you want to do, you can do, because it's just a program like anything else. For 3, the answer is "just write it the way you want it"... there isn't really an "answer" because that's not a problem in the first place, any more than it would be in C. And for 4, again, just do that then. Nothing's stopping you.