ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:122506] [Ruby Feature#21435] Kernel#optional as a conditional #then
@ 2025-06-10  7:30 Alexander.Senko (Alexander Senko) via ruby-core
  2025-06-10  8:56 ` [ruby-core:122507] " nobu (Nobuyoshi Nakada) via ruby-core
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Alexander.Senko (Alexander Senko) via ruby-core @ 2025-06-10  7:30 UTC (permalink / raw)
  To: ruby-core; +Cc: Alexander.Senko (Alexander Senko)

Issue #21435 has been reported by Alexander.Senko (Alexander Senko).

----------------------------------------
Feature #21435: Kernel#optional as a conditional #then
https://bugs.ruby-lang.org/issues/21435

* Author: Alexander.Senko (Alexander Senko)
* Status: Open
----------------------------------------
When chaining, I need sometimes to apply some changes conditionally, like this:
```ruby
@record = Record.find(record_id)
  .then { it.respond_to?(:decorate) ? it.decorate : it }
```

It would be great to DRY it a bit:
```ruby
@record = Record.find(record_id)
  .optional { it.decorate if it.respond_to? :decorate }
```

Reference implementation:
```ruby
# Yields self to the block and returns the result of the block if it’s
# truthy, and self otherwise.
def optional
  tap do
    result = yield(self) or next

    break result
  end
end
```

The name is discussible.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122507] [Ruby Feature#21435] Kernel#optional as a conditional #then
  2025-06-10  7:30 [ruby-core:122506] [Ruby Feature#21435] Kernel#optional as a conditional #then Alexander.Senko (Alexander Senko) via ruby-core
@ 2025-06-10  8:56 ` nobu (Nobuyoshi Nakada) via ruby-core
  2025-06-10 14:39 ` [ruby-core:122509] " Alexander.Senko (Alexander Senko) via ruby-core
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: nobu (Nobuyoshi Nakada) via ruby-core @ 2025-06-10  8:56 UTC (permalink / raw)
  To: ruby-core; +Cc: nobu (Nobuyoshi Nakada)

Issue #21435 has been updated by nobu (Nobuyoshi Nakada).


I agree that the pattern sometimes appears.
But the name `optional` feels kind of ambiguous or too generic, to me.

Alexander.Senko (Alexander Senko) wrote:
> Reference implementation:
> 
> ```ruby
> # Yields self to the block and returns the result of the block if it’s
> # truthy, and self otherwise.
> def optional
>   yield(self) or self
> end
> ```

Regarding `respond_to?`, IIRC, isn't ActiveSupport's `try` based on it?


----------------------------------------
Feature #21435: Kernel#optional as a conditional #then
https://bugs.ruby-lang.org/issues/21435#change-113730

* Author: Alexander.Senko (Alexander Senko)
* Status: Open
----------------------------------------
When chaining, I need sometimes to apply some changes conditionally, like this:
```ruby
@record = Record.find(record_id)
  .then { it.respond_to?(:decorate) ? it.decorate : it }
```

It would be great to DRY it a bit:
```ruby
@record = Record.find(record_id)
  .optional { it.decorate if it.respond_to? :decorate }
```

Reference implementation:
```ruby
# Yields self to the block and returns the result of the block if it’s
# truthy, and self otherwise.
def optional
  tap do
    result = yield(self) or next

    break result
  end
end
```

The name is discussible.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122509] [Ruby Feature#21435] Kernel#optional as a conditional #then
  2025-06-10  7:30 [ruby-core:122506] [Ruby Feature#21435] Kernel#optional as a conditional #then Alexander.Senko (Alexander Senko) via ruby-core
  2025-06-10  8:56 ` [ruby-core:122507] " nobu (Nobuyoshi Nakada) via ruby-core
@ 2025-06-10 14:39 ` Alexander.Senko (Alexander Senko) via ruby-core
  2025-06-10 19:19 ` [ruby-core:122510] " matheusrich (Matheus Richard) via ruby-core
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Alexander.Senko (Alexander Senko) via ruby-core @ 2025-06-10 14:39 UTC (permalink / raw)
  To: ruby-core; +Cc: Alexander.Senko (Alexander Senko)

Issue #21435 has been updated by Alexander.Senko (Alexander Senko).


nobu (Nobuyoshi Nakada) wrote in #note-1:
> Regarding `respond_to?`, IIRC, isn't ActiveSupport's `try` based on it?

Yes, it would be even simpler with Rails:
```ruby
@record = Record.find(record_id)
  .optional { it.try :decorate }
```

I just decided not to post Rails-specific code here.

----------------------------------------
Feature #21435: Kernel#optional as a conditional #then
https://bugs.ruby-lang.org/issues/21435#change-113732

* Author: Alexander.Senko (Alexander Senko)
* Status: Open
----------------------------------------
When chaining, I need sometimes to apply some changes conditionally, like this:
```ruby
@record = Record.find(record_id)
  .then { it.respond_to?(:decorate) ? it.decorate : it }
```

It would be great to DRY it a bit:
```ruby
@record = Record.find(record_id)
  .optional { it.decorate if it.respond_to? :decorate }
```

Reference implementation:
```ruby
# Yields self to the block and returns the result of the block if it’s
# truthy, and self otherwise.
def optional
  tap do
    result = yield(self) or next

    break result
  end
end
```

The name is discussible.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122510] [Ruby Feature#21435] Kernel#optional as a conditional #then
  2025-06-10  7:30 [ruby-core:122506] [Ruby Feature#21435] Kernel#optional as a conditional #then Alexander.Senko (Alexander Senko) via ruby-core
  2025-06-10  8:56 ` [ruby-core:122507] " nobu (Nobuyoshi Nakada) via ruby-core
  2025-06-10 14:39 ` [ruby-core:122509] " Alexander.Senko (Alexander Senko) via ruby-core
@ 2025-06-10 19:19 ` matheusrich (Matheus Richard) via ruby-core
  2025-06-10 20:33 ` [ruby-core:122511] " Alexander.Senko (Alexander Senko) via ruby-core
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: matheusrich (Matheus Richard) via ruby-core @ 2025-06-10 19:19 UTC (permalink / raw)
  To: ruby-core; +Cc: matheusrich (Matheus Richard)

Issue #21435 has been updated by matheusrich (Matheus Richard).


I'm sorry, I don't understand the use case, nor how it DRY things up.

The given example shaves off 1 character. What is optional doing? What is "optional" referring to?

----------------------------------------
Feature #21435: Kernel#optional as a conditional #then
https://bugs.ruby-lang.org/issues/21435#change-113733

* Author: Alexander.Senko (Alexander Senko)
* Status: Open
----------------------------------------
When chaining, I need sometimes to apply some changes conditionally, like this:
```ruby
@record = Record.find(record_id)
  .then { it.respond_to?(:decorate) ? it.decorate : it }
```

It would be great to DRY it a bit:
```ruby
@record = Record.find(record_id)
  .optional { it.decorate if it.respond_to? :decorate }
```

Reference implementation:
```ruby
# Yields self to the block and returns the result of the block if it’s
# truthy, and self otherwise.
def optional
  tap do
    result = yield(self) or next

    break result
  end
end
```

The name is discussible.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122511] [Ruby Feature#21435] Kernel#optional as a conditional #then
  2025-06-10  7:30 [ruby-core:122506] [Ruby Feature#21435] Kernel#optional as a conditional #then Alexander.Senko (Alexander Senko) via ruby-core
                   ` (2 preceding siblings ...)
  2025-06-10 19:19 ` [ruby-core:122510] " matheusrich (Matheus Richard) via ruby-core
@ 2025-06-10 20:33 ` Alexander.Senko (Alexander Senko) via ruby-core
  2025-06-11  9:38 ` [ruby-core:122513] " mame (Yusuke Endoh) via ruby-core
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Alexander.Senko (Alexander Senko) via ruby-core @ 2025-06-10 20:33 UTC (permalink / raw)
  To: ruby-core; +Cc: Alexander.Senko (Alexander Senko)

Issue #21435 has been updated by Alexander.Senko (Alexander Senko).


matheusrich (Matheus Richard) wrote in #note-3:
> I'm sorry, I don't understand the use case, nor how it DRY things up.
> 
> The given example shaves off 1 character. What is optional doing? What is "optional" referring to?

The idea is a) to **reduce cognitive complexity** by removing one trivial branch of `if`/`else` statement or an ugly ternary operator, and b) to **highlight explicitly that the statement is conditional** and may leave the result intact in some cases.

The method may improve readability of longer processing chains when some of the operations are applied conditionally. Without it, we have to repeat that same `else` branch for every conditional processing in a chain -- that looks a bit cumbersome and not very DRY for me.

I'm not a native speaker, sorry. IMO, `optional` indicates that execution is not guaranteed and depends on a condition. Maybe it could be called `maybe` instead.

----------------------------------------
Feature #21435: Kernel#optional as a conditional #then
https://bugs.ruby-lang.org/issues/21435#change-113734

* Author: Alexander.Senko (Alexander Senko)
* Status: Open
----------------------------------------
When chaining, I need sometimes to apply some changes conditionally, like this:
```ruby
@record = Record.find(record_id)
  .then { it.respond_to?(:decorate) ? it.decorate : it }
```

It would be great to DRY it a bit:
```ruby
@record = Record.find(record_id)
  .optional { it.decorate if it.respond_to? :decorate }
```

Reference implementation:
```ruby
# Yields self to the block and returns the result of the block if it’s
# truthy, and self otherwise.
def optional
  tap do
    result = yield(self) or next

    break result
  end
end
```

The name is discussible.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122513] [Ruby Feature#21435] Kernel#optional as a conditional #then
  2025-06-10  7:30 [ruby-core:122506] [Ruby Feature#21435] Kernel#optional as a conditional #then Alexander.Senko (Alexander Senko) via ruby-core
                   ` (3 preceding siblings ...)
  2025-06-10 20:33 ` [ruby-core:122511] " Alexander.Senko (Alexander Senko) via ruby-core
@ 2025-06-11  9:38 ` mame (Yusuke Endoh) via ruby-core
  2025-06-11 13:12 ` [ruby-core:122517] " Alexander.Senko (Alexander Senko) via ruby-core
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: mame (Yusuke Endoh) via ruby-core @ 2025-06-11  9:38 UTC (permalink / raw)
  To: ruby-core; +Cc: mame (Yusuke Endoh)

Issue #21435 has been updated by mame (Yusuke Endoh).


To be honest, when I see a code fragment like `.optional { it.decorate if it.respond_to? :decorate }`, I couldn't understand the intended behavior at all. Personally, I feel that this actually increases cognitive complexity rather than reducing it.

It's not just that the method name `optional` feels inappropriate. I think the existence of a method with such tricky behavior itself adds to cognitive complexity.

In terms of cognitive complexity, I find it much easier to understand when the logic is written out explicitly, even if it's more verbose, like this:

```ruby
@record = Record.find(record_id)
@record = @record.decorate if @record.respond_to?(:decorate)
```

----------------------------------------
Feature #21435: Kernel#optional as a conditional #then
https://bugs.ruby-lang.org/issues/21435#change-113736

* Author: Alexander.Senko (Alexander Senko)
* Status: Open
----------------------------------------
When chaining, I need sometimes to apply some changes conditionally, like this:
```ruby
@record = Record.find(record_id)
  .then { it.respond_to?(:decorate) ? it.decorate : it }
```

It would be great to DRY it a bit:
```ruby
@record = Record.find(record_id)
  .optional { it.decorate if it.respond_to? :decorate }
```

Reference implementation:
```ruby
# Yields self to the block and returns the result of the block if it’s
# truthy, and self otherwise.
def optional
  tap do
    result = yield(self) or next

    break result
  end
end
```

The name is discussible.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122517] [Ruby Feature#21435] Kernel#optional as a conditional #then
  2025-06-10  7:30 [ruby-core:122506] [Ruby Feature#21435] Kernel#optional as a conditional #then Alexander.Senko (Alexander Senko) via ruby-core
                   ` (4 preceding siblings ...)
  2025-06-11  9:38 ` [ruby-core:122513] " mame (Yusuke Endoh) via ruby-core
@ 2025-06-11 13:12 ` Alexander.Senko (Alexander Senko) via ruby-core
  2025-06-12  6:44 ` [ruby-core:122520] " Alexander.Senko (Alexander Senko) via ruby-core
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Alexander.Senko (Alexander Senko) via ruby-core @ 2025-06-11 13:12 UTC (permalink / raw)
  To: ruby-core; +Cc: Alexander.Senko (Alexander Senko)

Issue #21435 has been updated by Alexander.Senko (Alexander Senko).


mame (Yusuke Endoh) wrote in #note-5:
> In terms of cognitive complexity, I find it much easier to understand when the logic is written out explicitly, even if it's more verbose, like this:
> 
> ```ruby
> @record = Record.find(record_id)
> @record = @record.decorate if @record.respond_to?(:decorate)
> ```

Those who prefer iterative logic over method chains never needed `#then` as well.

----------------------------------------
Feature #21435: Kernel#optional as a conditional #then
https://bugs.ruby-lang.org/issues/21435#change-113738

* Author: Alexander.Senko (Alexander Senko)
* Status: Open
----------------------------------------
When chaining, I need sometimes to apply some changes conditionally, like this:
```ruby
@record = Record.find(record_id)
  .then { it.respond_to?(:decorate) ? it.decorate : it }
```

It would be great to DRY it a bit:
```ruby
@record = Record.find(record_id)
  .optional { it.decorate if it.respond_to? :decorate }
```

Reference implementation:
```ruby
# Yields self to the block and returns the result of the block if it’s
# truthy, and self otherwise.
def optional
  tap do
    result = yield(self) or next

    break result
  end
end
```

The name is discussible.



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122520] [Ruby Feature#21435] Kernel#optional as a conditional #then
  2025-06-10  7:30 [ruby-core:122506] [Ruby Feature#21435] Kernel#optional as a conditional #then Alexander.Senko (Alexander Senko) via ruby-core
                   ` (5 preceding siblings ...)
  2025-06-11 13:12 ` [ruby-core:122517] " Alexander.Senko (Alexander Senko) via ruby-core
@ 2025-06-12  6:44 ` Alexander.Senko (Alexander Senko) via ruby-core
  2025-06-14 14:39 ` [ruby-core:122533] " zverok (Victor Shepelev) via ruby-core
  2025-06-15  0:02 ` [ruby-core:122534] [Ruby Feature#21435] Kernel#then_try " Alexander.Senko (Alexander Senko) via ruby-core
  8 siblings, 0 replies; 10+ messages in thread
From: Alexander.Senko (Alexander Senko) via ruby-core @ 2025-06-12  6:44 UTC (permalink / raw)
  To: ruby-core; +Cc: Alexander.Senko (Alexander Senko)

Issue #21435 has been updated by Alexander.Senko (Alexander Senko).


@zverok, what do you think about it? May `#then` get a conditional counterpart?

----------------------------------------
Feature #21435: Kernel#optional as a conditional #then
https://bugs.ruby-lang.org/issues/21435#change-113742

* Author: Alexander.Senko (Alexander Senko)
* Status: Open
----------------------------------------
## What

When chaining, I need sometimes to apply some changes conditionally, like this:
```ruby
@record = Record.find(record_id)
  .then { it.respond_to?(:decorate) ? it.decorate : it }
```

It would be great to DRY it a bit:
```ruby
@record = Record.find(record_id)
  .optional { it.decorate if it.respond_to? :decorate }
```

Or, even shorter for Rails users:
```ruby
@record = Record.find(record_id)
  .optional { it.try :decorate }
```

## Why

The intent is to **make it visible at a glance that a statement _may_ affect the result**, but not necessarily does so. Without the proposed method, one needs to read and parse the whole block to know that.

It should help to read longer processing chains, for those who prefer chains and `#then` to plain old iterative approach.

## Naming

It is discussible. I have just two ideas yet:
- `optional`
- `maybe`

## Reference implementation

```ruby
# Yields self to the block and returns the result of the block if it’s
# truthy, and self otherwise.
def optional
  tap do
    result = yield(self) or next

    break result
  end
end
```



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122533] [Ruby Feature#21435] Kernel#optional as a conditional #then
  2025-06-10  7:30 [ruby-core:122506] [Ruby Feature#21435] Kernel#optional as a conditional #then Alexander.Senko (Alexander Senko) via ruby-core
                   ` (6 preceding siblings ...)
  2025-06-12  6:44 ` [ruby-core:122520] " Alexander.Senko (Alexander Senko) via ruby-core
@ 2025-06-14 14:39 ` zverok (Victor Shepelev) via ruby-core
  2025-06-15  0:02 ` [ruby-core:122534] [Ruby Feature#21435] Kernel#then_try " Alexander.Senko (Alexander Senko) via ruby-core
  8 siblings, 0 replies; 10+ messages in thread
From: zverok (Victor Shepelev) via ruby-core @ 2025-06-14 14:39 UTC (permalink / raw)
  To: ruby-core; +Cc: zverok (Victor Shepelev)

Issue #21435 has been updated by zverok (Victor Shepelev).


@Alexander.Senko I don't think my opinion weights much here, but here it is, nevertheless.

When writing in "chaining" style (which I know is not everybody's favorite), "do this transformation/next step conditionally" is indeed a frequent thing that comes to mind.

There are two cases: a) the condition depends on the `self` (the current step of the chain) and b) the condition doesn't depend on it.

For the latter, I would've liked (though I don't feel strongly enough to propose it) something like `then_if`:

```ruby
def save(data, path, compress: true)
  data
    .do_something
    .do_something_else
    .to_json
    .then_if(compress) { Compression.call(it) } # skipped just returning self if condition is false
    .then { File.write(path, it) }
end
```

For a case when the step depends on `self` in that step, I don't have an idea of a good syntax, TBH. Maybe something like this (though purely theoretical, I wouldn't really want to add more lambdas to the code this way, both for "too much punctuation" and performance concerns):

```ruby
  data
    .do_something
    .do_something_else
    .to_json
    .then_if(-> { it.size > LIMIT }) { Compression.call(it) }
    .then { File.write(path, it) }
```

I understand where the idea of `optional` is coming from, but for me, both the name and the usage seem kinda ambiguous. 

In any case, I rarely miss this option that much, and never had a good idea of a "proper" syntax/API to allow that.

----------------------------------------
Feature #21435: Kernel#optional as a conditional #then
https://bugs.ruby-lang.org/issues/21435#change-113759

* Author: Alexander.Senko (Alexander Senko)
* Status: Open
----------------------------------------
## What

When chaining, I need sometimes to apply some changes conditionally, like this:
```ruby
@record = Record.find(record_id)
  .then { it.respond_to?(:decorate) ? it.decorate : it }
```

It would be great to DRY it a bit:
```ruby
@record = Record.find(record_id)
  .optional { it.decorate if it.respond_to? :decorate }
```

Or, even shorter for Rails users:
```ruby
@record = Record.find(record_id)
  .optional { it.try :decorate }
```

## Why

The intent is to **make it visible at a glance that a statement _may_ affect the result**, but not necessarily does so. Without the proposed method, one needs to read and parse the whole block to know that.

It should help to read longer processing chains, for those who prefer chains and `#then` to plain old iterative approach.

## Naming

It is discussible. I have just two ideas yet:
- `optional`
- `maybe`

## Reference implementation

```ruby
# Yields self to the block and returns the result of the block if it’s
# truthy, and self otherwise.
def optional
  tap do
    result = yield(self) or next

    break result
  end
end
```



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [ruby-core:122534] [Ruby Feature#21435] Kernel#then_try as a conditional #then
  2025-06-10  7:30 [ruby-core:122506] [Ruby Feature#21435] Kernel#optional as a conditional #then Alexander.Senko (Alexander Senko) via ruby-core
                   ` (7 preceding siblings ...)
  2025-06-14 14:39 ` [ruby-core:122533] " zverok (Victor Shepelev) via ruby-core
@ 2025-06-15  0:02 ` Alexander.Senko (Alexander Senko) via ruby-core
  8 siblings, 0 replies; 10+ messages in thread
From: Alexander.Senko (Alexander Senko) via ruby-core @ 2025-06-15  0:02 UTC (permalink / raw)
  To: ruby-core; +Cc: Alexander.Senko (Alexander Senko)

Issue #21435 has been updated by Alexander.Senko (Alexander Senko).

Subject changed from Kernel#optional as a conditional #then to Kernel#then_try as a conditional #then
Description updated

`optional` -> `then_try`

----------------------------------------
Feature #21435: Kernel#then_try as a conditional #then
https://bugs.ruby-lang.org/issues/21435#change-113760

* Author: Alexander.Senko (Alexander Senko)
* Status: Open
----------------------------------------
## What

When chaining, I need sometimes to apply some changes conditionally, like this:
```ruby
@record = Record.find(record_id)
  .then { it.respond_to?(:decorate) ? it.decorate : it }
```

It would be great to DRY it a bit:
```ruby
@record = Record.find(record_id)
  .then_try { it.decorate if it.respond_to? :decorate }
```

Or, even shorter for Rails users:
```ruby
@record = Record.find(record_id)
  .then_try { it.try :decorate }
```

## Why

The intent is to **make it visible at a glance that a statement _may_ affect the result**, but not necessarily does so. Without the proposed method, one needs to read and parse the whole block to know that.

It should help to read longer processing chains, for those who prefer chains and `#then` to plain old iterative approach.

## Naming

It is discussible. I have just two ideas yet:
- `then_try`
- `optional`
- `maybe`

## Reference implementation

```ruby
# Yields self to the block and returns the result of the block if it’s
# truthy, and self otherwise.
def then_try
  tap do
    result = yield(self) or next

    break result
  end
end
```



-- 
https://bugs.ruby-lang.org/
______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-06-15  0:03 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-06-10  7:30 [ruby-core:122506] [Ruby Feature#21435] Kernel#optional as a conditional #then Alexander.Senko (Alexander Senko) via ruby-core
2025-06-10  8:56 ` [ruby-core:122507] " nobu (Nobuyoshi Nakada) via ruby-core
2025-06-10 14:39 ` [ruby-core:122509] " Alexander.Senko (Alexander Senko) via ruby-core
2025-06-10 19:19 ` [ruby-core:122510] " matheusrich (Matheus Richard) via ruby-core
2025-06-10 20:33 ` [ruby-core:122511] " Alexander.Senko (Alexander Senko) via ruby-core
2025-06-11  9:38 ` [ruby-core:122513] " mame (Yusuke Endoh) via ruby-core
2025-06-11 13:12 ` [ruby-core:122517] " Alexander.Senko (Alexander Senko) via ruby-core
2025-06-12  6:44 ` [ruby-core:122520] " Alexander.Senko (Alexander Senko) via ruby-core
2025-06-14 14:39 ` [ruby-core:122533] " zverok (Victor Shepelev) via ruby-core
2025-06-15  0:02 ` [ruby-core:122534] [Ruby Feature#21435] Kernel#then_try " Alexander.Senko (Alexander Senko) via ruby-core

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).