ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:119469] [Ruby master Feature#20786] Flow chaining with "then" keyword
@ 2024-10-06 14:04 lpogic via ruby-core
  2024-10-07  2:18 ` [ruby-core:119471] " nobu (Nobuyoshi Nakada) via ruby-core
  2024-10-07  9:39 ` [ruby-core:119474] " lpogic via ruby-core
  0 siblings, 2 replies; 3+ messages in thread
From: lpogic via ruby-core @ 2024-10-06 14:04 UTC (permalink / raw)
  To: ruby-core; +Cc: lpogic

Issue #20786 has been reported by lpogic (Łukasz Pomietło).

----------------------------------------
Feature #20786: Flow chaining with "then" keyword
https://bugs.ruby-lang.org/issues/20786

* Author: lpogic (Łukasz Pomietło)
* Status: Open
----------------------------------------
Hi,
I would like to propose using the *"then"* keyword to create chained flows.

### Background:
Original idea: https://bugs.ruby-lang.org/issues/20770#change-110056
However, the concept has changed slightly since then. I now assume that rhs is only calculated when lhs is true, which seems more compatible with the current usage of *"then"* keyword.
I would also like to move the discussion here as it may cause unnecessary chaos there.

### Basics:
In the canonical version, *"then"* is used in a *"begin..end"* block to split it into steps.
When the result of the last expression before *"then"* is not *"false"* or *"nil"*, it is passed as an argument to the next step. Otherwise, all subsequent steps are skipped.
The result of the *"begin..end"* block is the result of the last expression, or *"false"*/*"nil"* if any step was skipped.

### Example:
``` ruby
# original code source: https://github.com/ruby/ruby/blob/a6383fbe1628bdaa9ec6a30b3baa60dd7430b461/lib/ipaddr.rb#L181C1-L185C6
def include?(other)
  other = coerce_other(other)
  return false unless other.family == family
  begin_addr <= other.begin_addr && end_addr >= other.end_addr
end

# with chained flow
def include?(other)
  begin
    coerce_other(other)
  then => coerced  # then => name   is my proposition of argument naming syntax
    return false unless coerced.family == family
    coerced
  then => it
    begin_addr <= it.begin_addr && end_addr >= it.end_addr
  end
end
```

Please treat the canonical form as a starting point. As further improvements I would see:
- implicit block argument name
- implicit begin
- beginless *"then"*, when lhs is just expression

### Example with futher improvements:
``` ruby
def include?(other)
  coerce_other(other)
then 
  it.family == family ? it : false
then
  begin_addr <= it.begin_addr && end_addr >= it.end_addr
end

# or
def include?(other)
  coerce_other other then
    return false unless it.family == family
    begin_addr <= it.begin_addr && end_addr >= it.end_addr
  end
end
```

### *"then"* keyword as the pipeline operator:
Under certain conditions, "then" could offer similar capabilities to the pipeline operator of functional languages:
``` ruby
# assuming that "foo" and "bar" never return "false" or "nil"
# instead of writing this:
baz("string", bar(foo(), 2), 5)
# you could write this:
begin foo() 
then bar(it, 2) 
then baz("string", it, 5) 
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] 3+ messages in thread

* [ruby-core:119471] [Ruby master Feature#20786] Flow chaining with "then" keyword
  2024-10-06 14:04 [ruby-core:119469] [Ruby master Feature#20786] Flow chaining with "then" keyword lpogic via ruby-core
@ 2024-10-07  2:18 ` nobu (Nobuyoshi Nakada) via ruby-core
  2024-10-07  9:39 ` [ruby-core:119474] " lpogic via ruby-core
  1 sibling, 0 replies; 3+ messages in thread
From: nobu (Nobuyoshi Nakada) via ruby-core @ 2024-10-07  2:18 UTC (permalink / raw)
  To: ruby-core; +Cc: nobu (Nobuyoshi Nakada)

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


It looks to conflict inside `if`.

----------------------------------------
Feature #20786: Flow chaining with "then" keyword
https://bugs.ruby-lang.org/issues/20786#change-110088

* Author: lpogic (Łukasz Pomietło)
* Status: Open
----------------------------------------
Hi,
I would like to propose using the *"then"* keyword to create chained flows.

### Background:
Original idea: https://bugs.ruby-lang.org/issues/20770#change-110056
However, the concept has changed slightly since then. I now assume that rhs is only calculated when lhs is true, which seems more compatible with the current usage of *"then"* keyword.
I would also like to move the discussion here as it may cause unnecessary chaos there.

### Basics:
In the canonical version, *"then"* is used in a *"begin..end"* block to split it into steps.
When the result of the last expression before *"then"* is not *"false"* or *"nil"*, it is passed as an argument to the next step. Otherwise, all subsequent steps are skipped.
The result of the *"begin..end"* block is the result of the last expression, or *"false"*/*"nil"* if any step was skipped.

### Example:
``` ruby
# original code source: https://github.com/ruby/ruby/blob/a6383fbe1628bdaa9ec6a30b3baa60dd7430b461/lib/ipaddr.rb#L181C1-L185C6
def include?(other)
  other = coerce_other(other)
  return false unless other.family == family
  begin_addr <= other.begin_addr && end_addr >= other.end_addr
end

# with chained flow
def include?(other)
  begin
    coerce_other(other)
  then => coerced  # then => name   is my proposition of argument naming syntax
    return false unless coerced.family == family
    coerced
  then => it
    begin_addr <= it.begin_addr && end_addr >= it.end_addr
  end
end
```

Please treat the canonical form as a starting point. As further improvements I would see:
- implicit block argument name
- implicit begin
- beginless *"then"*, when lhs is just expression

### Example with futher improvements:
``` ruby
def include?(other)
  coerce_other(other)
then 
  it.family == family ? it : false
then
  begin_addr <= it.begin_addr && end_addr >= it.end_addr
end

# or
def include?(other)
  coerce_other other then
    return false unless it.family == family
    begin_addr <= it.begin_addr && end_addr >= it.end_addr
  end
end
```

### *"then"* keyword as the pipeline operator:
Under certain conditions, "then" could offer similar capabilities to the pipeline operator of functional languages:
``` ruby
# assuming that "foo" and "bar" never return "false" or "nil"
# instead of writing this:
baz("string", bar(foo(), 2), 5)
# you could write this:
begin foo() 
then bar(it, 2) 
then baz("string", it, 5) 
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] 3+ messages in thread

* [ruby-core:119474] [Ruby master Feature#20786] Flow chaining with "then" keyword
  2024-10-06 14:04 [ruby-core:119469] [Ruby master Feature#20786] Flow chaining with "then" keyword lpogic via ruby-core
  2024-10-07  2:18 ` [ruby-core:119471] " nobu (Nobuyoshi Nakada) via ruby-core
@ 2024-10-07  9:39 ` lpogic via ruby-core
  1 sibling, 0 replies; 3+ messages in thread
From: lpogic via ruby-core @ 2024-10-07  9:39 UTC (permalink / raw)
  To: ruby-core; +Cc: lpogic

Issue #20786 has been updated by lpogic (Łukasz Pomietło).


You're right. Thats why I proposed to evaluate rhs conditionally.
The canonical form has a clear *beginning* and *end*, so I assume your comment refers to the beginningless form. Let's look the following example:
``` ruby
foo then bar end
```
The "bar" is evaluated only if "foo" is true, so it does quiet the same what:
``` ruby
if foo then bar end
```
does (except that first form result is *"false"* if *"foo"* returns *"false"*).

Another examples:
``` ruby
# chained flow in condition:
if (foo then baz end) then
  bar
end

# chained flow in statement:
if foo
  bar then baz end  # please note that in the beginless form the expression is required on the left side of the first "then"
end
```

I think the most questionable case is this:
``` ruby
if foo then bar then baz end end
```
So I see 2 solutions:
1) If "if" is used, the first "then" belongs to another flow, so the case is the same as one above.
2) "if" can also be divided into steps, so the case is as follows:
``` ruby
if foo
then
  bar
then
  baz
end # please note that the second "end" has been removed
```
I'm not sure which one would be better. In any case, the chain flow would allow for a notation like this:
``` ruby
begin
  foo
then
  bar
then
  baz
  ready?
then
  p "ok"
end
```
instead of this:
``` ruby
if foo
  if bar
    baz
    if ready?
      p "ok"
    end
  end
end
```

There is also the issue of the "else" branch. Can be an alternative path for the last "then" branch or for all:
``` ruby
# the case:
if foo
then bar
then
  baz
  ready?
then p "ok"
else p "fail"
end

# "else" as an alternative to the last branch:
if foo
  if bar
    baz
    if ready?
      p "ok"
    else
      p "fail"
    end
  end
end

# "else" as an alternative to all branches:
if foo
  if bar
    baz
    if ready?
      p "ok"
    else
      p "fail"
    end
  else 
    p "fail"
  end
else
  p "fail"
end
```


Finally, I'm not sure if "then" rhs should be evaluated conditionally, as this could sometimes cause problems in applications like the pipeline operator. Maybe use a different keyword for conditionality, e.g. "andthen"? I believe it should be carefully analyzed.

----------------------------------------
Feature #20786: Flow chaining with "then" keyword
https://bugs.ruby-lang.org/issues/20786#change-110091

* Author: lpogic (Łukasz Pomietło)
* Status: Open
----------------------------------------
Hi,
I would like to propose using the *"then"* keyword to create chained flows.

### Background:
Original idea: https://bugs.ruby-lang.org/issues/20770#change-110056
However, the concept has changed slightly since then. I now assume that rhs is only calculated when lhs is true, which seems more compatible with the current usage of *"then"* keyword.
I would also like to move the discussion here as it may cause unnecessary chaos there.

### Basics:
In the canonical version, *"then"* is used in a *"begin..end"* block to split it into steps.
When the result of the last expression before *"then"* is not *"false"* or *"nil"*, it is passed as an argument to the next step. Otherwise, all subsequent steps are skipped.
The result of the *"begin..end"* block is the result of the last expression, or *"false"*/*"nil"* if any step was skipped.

### Example:
``` ruby
# original code source: https://github.com/ruby/ruby/blob/a6383fbe1628bdaa9ec6a30b3baa60dd7430b461/lib/ipaddr.rb#L181C1-L185C6
def include?(other)
  other = coerce_other(other)
  return false unless other.family == family
  begin_addr <= other.begin_addr && end_addr >= other.end_addr
end

# with chained flow
def include?(other)
  begin
    coerce_other(other)
  then => coerced  # then => name   is my proposition of argument naming syntax
    return false unless coerced.family == family
    coerced
  then => it
    begin_addr <= it.begin_addr && end_addr >= it.end_addr
  end
end
```

Please treat the canonical form as a starting point. As further improvements I would see:
- implicit block argument name
- implicit begin
- beginless *"then"*, when lhs is just expression

### Example with futher improvements:
``` ruby
def include?(other)
  coerce_other(other)
then 
  it.family == family ? it : false
then
  begin_addr <= it.begin_addr && end_addr >= it.end_addr
end

# or
def include?(other)
  coerce_other other then
    return false unless it.family == family
    begin_addr <= it.begin_addr && end_addr >= it.end_addr
  end
end
```

### *"then"* keyword as the pipeline operator:
Under certain conditions, "then" could offer similar capabilities to the pipeline operator of functional languages:
``` ruby
# assuming that "foo" and "bar" never return "false" or "nil"
# instead of writing this:
baz("string", bar(foo(), 2), 5)
# you could write this:
begin foo() 
then bar(it, 2) 
then baz("string", it, 5) 
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] 3+ messages in thread

end of thread, other threads:[~2024-10-07  9:40 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-10-06 14:04 [ruby-core:119469] [Ruby master Feature#20786] Flow chaining with "then" keyword lpogic via ruby-core
2024-10-07  2:18 ` [ruby-core:119471] " nobu (Nobuyoshi Nakada) via ruby-core
2024-10-07  9:39 ` [ruby-core:119474] " lpogic 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).