Issue #15557 has been updated by shevegen (Robert A. Heiler).


Normally I love sawa's suggestions. :)

However had, in this case, I am a little bit biased, which I may explain.

First, I should start here by agreeing with sawa in regards to the use case noted on top is quite common -
I use it myself a lot, but I see it in other people's ruby code as well:

    foo = default_definition
    foo = some_method(foo) if some_condition(foo)
    foo = another_method(foo) if another_condition(foo)

Or perhaps in a simpler variant, more often this:

    foo = default_definition
    foo = some_method(foo) if some_condition
    foo = another_method(foo) if another_condition

These can be method calls too:
 
    foo = new_colour if use_colours?

I think the latter can be quite common if people wish to determine
what users may want to use; e. g. from a commandline interface,
like if you start a project with commandline flags --disable-colours
or any other flag. So yes, I think this can be quite common.

sawa gave an alternative proposal:

    .when{|foo| some_condition(foo)}
    .then{|foo| some_method(foo)}
    .when{|foo| another_condition(foo)}
    .then{|foo| another_method(foo)}

Now I should say that I personally actually prefer the "oldschool" variant
since, to me personally, the intent is more clear. Another minor concern I
have is that .when and .then are not so easy to distinguish visually; when
is also used in case/when menu interfaces, which I love (so I am not that
enthusiastic about using more "when" in other places in ruby, to be 
honest).

Which variant to prefer is of course heavily up to the individual person
at hand but in my opinion I think using mixed .when and .then is not as
easy or straightforward than the other variant.

Nobu suggested:

    .when(->(foo) {some_condition(foo)}) {|foo| some_method(foo)}

Here I can not comment much on it since I feel that in my own projects
the "->" always felt odd. Combining it with .when would make the code
even more odd (to me). Obviously the milage of other people may differ
here, so this is again a personal preference.

Aside from syntactic considerations, I also feel that the variant with
.when(->(foo) {another_condition(foo)}) {|foo| another_method(foo)} is
harder to break up in the head than the oldschool if/else variant checks.

sawa also suggested:

    a.some_condition ? a : b

would rewritten as:

    a.when(&:some_condition).then{b}

which I think is not ideal either, because it is longer. I myself avoid the ternary
operator, though, because it always takes my brain a little longer than e. g.
just an if; and a return (if we can avoid using an else altogether, or even better,
to simply use a conditional method call such as one that uses a boolean 
return, if we can avoid if/else branching altogether, which can lead to even
simpler code layouts). But this is again up to one's personal preference, and
since I am biased I am admittedly not trying to find good alternative point of
views. :)


Do note that I am not at all against sawa's suggestion or statement that "would be nice if
we can write this as a method chain". I think that part is fine; avoiding if/else branches
can be good in some cases. I am just somewhat concerned in regards to the syntax-verbosity 
and that it may lead to more complicated code - the intent in the proposal otherwise is
perfectly fine. This is of course just my personal opinion. :)

zverok just wrote this as I was about to finish my comment ... :P
> for the sake of nice syntax, e.g. something.if(&:cond?) on itself doesn't produce an
> easily explainable/reusable object.

Here we have to ask whether the alternative syntax proposals are better or really
are a "nice syntax". I have my doubts. :)

Also note that:

    something.if
    something.else
    something.if(&:cond?) 
    something.else(&:cond?) 

I consider actually significantly worse syntax. :D

----------------------------------------
Feature #15557: A new class that stores a condition and the previous receiver
https://bugs.ruby-lang.org/issues/15557#change-76470

* Author: sawa (Tsuyoshi Sawada)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
I often see code like this:

 ```ruby
foo = default_definition
foo = some_method(foo) if some_condition(foo)
foo = another_method(foo) if another_condition(foo)
...
```

It would be nice if we can write this as a method chain. Since we now have the method `then`, I thought it would be a nice fit to introduce a method called `when`, such that putting it right in front of `then` would execute the `then` method as ordinarily only when the condition is satisfied, and returns the previous receiver otherwise so that the code above can be rewritten as:

```ruby
foo =
default_definition
.when{|foo| some_condition(foo)}
.then{|foo| some_method(foo)}
.when{|foo| another_condition(foo)}
.then{|foo| another_method(foo)}
```

This proposal is also a generalization of what I intended to cover by https://bugs.ruby-lang.org/issues/13807. That is,

```ruby
a.some_condition ? a : b
```

would rewritten as:

```ruby
a.when(&:some_condition).then{b}
```

The proposal can be implemented by introducing a class called `Condition`, which stores a condition and the previous receiver, and works with `then` in a particular way.

```ruby
class Object
  def when
    Condition.new(self, yield(self))
  end
end

class Condition
  def initialize default, condition
    @default, @condition = default, condition
  end

  def then
    @condition ? yield(@default) : @default
  end
end
```

And additionally, if we introduce a negated method `unless` (or `else`) as follows:

```ruby
class Object
  def unless
    Condition.new(self, !yield(self))
  end
end
```

then we can use that for purposes such as validation of a variable as follows:

```ruby
bar =
gets
.unless{|bar| some_validation(bar)}
.then{raise "The input is bad."}
.unless{|bar| another_validation(bar)}
.then{raise "The input is bad in another way."}
```




-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>