8 ms·
So... try/catch?
by fortythirteen 8y ago
So... try/catch?
- fredley 8y agoYes, but in a functional paradigm.
- amelius 8y agoI'm wondering: why not make every returned value implicitly of type Either with Left some generic type? In most cases, I'm not interested in the types of exception that can be thrown. And when I need the type of an exception, a runtime check suffices in almost all cases. Let's not make exception handling more cumbersome than it needs to be, and only invoke verbose mechanisms when necessary.
- beojan 8y agoNot all functions can produce an error. If you add two arbitrary precision integers, you're not getting an error.
- amelius 8y agoSo (+) would be defined as adding its operands, except if one of them is an error (in which case the result would be an error).
- jjaredsimpson 8y agoEither is a sum type. What if I don't want that. It's not about handling errors per se, but modeling the type.
- amelius 8y agoYes, I understand. But by "implicitly" I really meant that. You don't see the type, only the compiler does. The programmer only sees the non-error part of the type (the "Right" part of Either).
- jjaredsimpson 8y agoHow could this work? I specify an interface to an api. C Foo(A a) = ... Someone then provides me with some not-A error value. What should I do? What do you mean by implicit.
- KirinDave 8y ago> Either is a sum type. What if I don't want that. Product types work as well, if you want. But unless your return type is a monoid (or like, a semilattice maybe?) then you're going to end up with Sum Types somewhere. The vast, vast majority of software engineers and students work with sum types every day and they're totally fine with them. Most languages implicitly sum many values with null.
- KirinDave 8y agoUnless you're running in a multi-threaded environment or run out of memory?
- lauritzsh 8y agoThese sounds like exceptions rather than errors to me. Difference being, exceptions truly are exceptional and not anticipated; like running out of memory. How do you even handle that? Would probably let it crash and restart (Erlang model). Errors is something you anticipate can happen, such as getting 4xx-5xx back from the server and know what to do in those cases. Then it's fair to say that you definitely can't get an error by adding two numbers.
- KirinDave 8y ago> How do you even handle that? You request a smaller block of memory? Not everyone is always requesting the minimum they need right now. > Would probably let it crash and restart (Erlang model) The Erlang model is to let a partial failure trigger a less complex part of the system to resume that logic based on a strategy. > Then it's fair to say that you definitely can't get an error by adding two numbers. I mean, you can also accept garbage back, I guess?
- lmm 8y ago> I'm wondering: why not make every returned value implicitly of type Either with Left some generic type? Because then you lose the ability to have code that doesn't error. If you explicitly mark those parts of your code that can error, you can then start to decouple those parts that can error from those that will never error. E.g. you can separate the business logic which figures out which database query you need to run (which can't error) from the code that actually performs database queries (which contains timeouts, retry logic etc.), and then you can test those two things separately and make sure you've covered all the cases. Whereas with pervasive/implicit exceptions, every line of your business logic might throw an exception (or might not throw an exception now, but later be refactored to throw one), so you need to test a combinatorial number of cases (or, more likely, your tests won't cover some code paths).
- deleted 8y ago[deleted]
- lmm 8y agoWrote a reply to a deleted comment, figured it was worth keeping to expand on my point: > - what if the tables / schema / etc doesn't exist? Then figuring out which query to run won't fail, executing the query will fail; the whole point was to separate the two. > - what if the logic which selects the DB query returns no query? You make that impossible, if that's not a valid answer. (If that is a valid answer then you use a return type that represents that - Maybe/Option - and then you're forced to handle that porperly). Most programming languages don't even permit a function to "return no value" so that's already not a problem. (Some programming languages permit a function to return "null" or loop forever; don't use those languages). > - what if the logic attempts to pull a value from an out of bounds array (or similar fail)? Sure you would check the bounds before querying the array but why did you get to a situation where you tried to pull an out of bounds index in the first place? So you don't get into that situation: you only use array indices that are valid by construction, you don't give yourself any way to construct an invalid index. (Ensuring that indices into a specific array are a different type from indices into any other array is a useful first step. Of course, in practice you probably avoid using arrays and indices at all; if you use higher-level operations like map and reduce then there aren't any indices to worry about). (Very occasionally there may be cases where you can't avoid having to do something that might error, in which case what you want to do is fail-fast, erlang style. But if you're doing what I'm advocating, this will be your only option: the point of all the above is that we make invalid states unrepresentable, so when we get into an invalid state it's simply impossible to continue. And really the vast majority of the time - every time actually, in my experience - you can find a way to write the code so that it can't error, if you actually try).
- KirinDave 8y agoThis just isn't true at all. Exceptions use a totally different mechanism from railway-oriented programming, have very different constraints, and different performance characteristics. Please don't entertain these silly posts. At best, it only grants legitimacy to a crassly presented idea.
- steego 8y agoThere's a difference. First, you're not incurring a large runtime penalty, so this works better in higher performance scenarios. Second, you're trapping and propagating successful values in a monad, which means your obligating the caller to handle both success and error conditions.
- jstimpfle 8y agoThe performance criticism is a common one, but I don't think most people actually know what they're talking about. I certainly don't, since I don't use exceptions, but I've heard many respected engineers say that they needn't be slow (it depends greatly on implementation). It shouldn't matter much anyway: You usually don't optimize for exceptional cases, especially if they lead to program termination. And furthermore, you should not make heavy use of exceptions anyway :-). They lead to non-local control flow that is extremely hard to follow. They are nice for short scripts where they print a stack trace and abort the program. But they are a huge maintenance burden in larger programs.
- steego 8y agoYou are correct that the performance of exceptions isn't typically needed because exceptions aren't meant to be thrown that often. They're exceptional. Railway oriented programming is better suited when errors are much more common in the ringtone and when their handling is a fundamental part of the rules you're encoding. One friend uses a Railway oriented approach precisely because throwing that many exceptions per second (I don't know the exact number) would severely undermine the performance of their system.
- jstimpfle 8y agoWhat is an example of exception that happens many times per second? And where it could not be easily avoided to step on the error path at all?
- steego 8y agoSo I'm not talking about exceptions, I'm talking about talking about errors. You can easily generate thousands of errors per second if you're simply validating and routing large streams of data.
- KirinDave 8y agoIt's about as far from try/catch as you can get while still being code.