Feature #20786
closedFlow chaining with "then" keyword
Description
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:¶
# 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:¶
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:
# 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