5 Comments
User's avatar
Julik's avatar

If your procedure does not run in a very tight loop, Exceptions for flow control in Ruby are fine - because they replace the Either. If you rescue exceptions according to their class (using matching of some description) you get a very good reproduction of an Either, much better than the approach with tuples of `[:ok, :result] and `[:error, :message]`. One of the unpleasant aspects of those tuples is that nothing prevents you from having a `[:ok, :error_message]` or a `[:error, :result]` creep in (say hello to the Go error handling). So I would do it with exceptions and a reduce - if your pipeline needs to be composable. If it isn't, maybe a straight-ahead `input = do_thing(input)` per line would be even simpler.

```ruby

calls = [

-> (input) { do_thing(input) },

-> (input) { do_another_thing(input) },

-> (input) { do_yet_another_thing(input) },

]

result = calls.inject("hello!") do |input_from_previous, callable|

callable.(input_from_previous)

end

```

Expand full comment
Julik's avatar

If your procedure does not run in a very tight loop, Exceptions for flow control in Ruby are fine - because they replace the Either. If you rescue exceptions according to their class (using matching of some description) you get a very good reproduction of an Either, much better than the approach with tuples of `[:ok, :result] and `[:error, :message]`. One of the unpleasant aspects of those tuples is that nothing prevents you from having a `[:ok, :error_message]` or a `[:error, :result]` creep in (say hello to the Go error handling). So I would do it with exceptions and a reduce:

```ruby

calls = [

-> (input) { do_thing(input) },

-> (input) { do_another_thing(input) },

-> (input) { do_yet_another_thing(input) },

]

result = calls.inject("hello!") do |input_from_previous, callable|

callable.(input_from_previous)

end

```

Expand full comment
Pragdave's avatar

That's a perfectly valid idea. I have always been nervous when using exceptions for control flow, if for no other reason tan their nonlocal nature makes it harder to reason about the code. I agree that in this case, if they are kept local to this one module, and always trigger an end of processing, they'd be OK.

As for the [ ok, :error_message ] issue, there's always Ruby's pattern matching...

Expand full comment
Julik's avatar

There is, but I haven't really taken on to Erlang-style destructuring. It is amazing for deconstructing a network packet or a struct of fixed offsets, but using it as a dispatch mechanism... didn't stick with me.

Expand full comment
Pragdave's avatar

Ah, there's come a moment when you'll suddenly find yourself writing more case statements with patterns than if statements. Not only does it linearize what could be a chain of `elsif`s, but it also lets you extract values that you need. Highly recommended.

Expand full comment