* [ruby-core:118535] [Ruby master Feature#20625] Object#chain_of
@ 2024-07-10 18:56 matheusrich (Matheus Richard) via ruby-core
2024-07-11 10:10 ` [ruby-core:118558] " zverok (Victor Shepelev) via ruby-core
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: matheusrich (Matheus Richard) via ruby-core @ 2024-07-10 18:56 UTC (permalink / raw)
To: ruby-core; +Cc: matheusrich (Matheus Richard)
Issue #20625 has been reported by matheusrich (Matheus Richard).
----------------------------------------
Feature #20625: Object#chain_of
https://bugs.ruby-lang.org/issues/20625
* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
## Motivation
It's often common to traverse a tree/list-like structure in order to get a chain
of elements. This proposal is to add a method to `Object` that allows collecting
a chain of elements by applying a block to each element. It doesn't require the
root element to be an instance of a specific class or respond to a specific
protocol.
I think this method could be useful in many cases, since hierarchies like this
are common in codebases (e.g. file systems, organizations structures, commit
histories, breadcrumbs in web apps, configuration hierarchies, etc.).
Here are some examples extracted from real codebases (simplified for the sake of
the example/privacy):
```rb
# Given a file system structure, get the breadcrumbs of a file or directory
def breadcrumbs(root)
crumbs = []
current = root
while current
crumbs << current
current = current.parent_dir
end
crumbs
end
# Given an employee, get the hierarchy of managers
def hierarchy(employee)
hierarchy = []
current = employee
while current
hierarchy << current
current = current.manager
end
hierarchy
end
```
## Implementation
The implementation in Ruby could look like this:
```rb
class Object
def chain_of(&block)
chain = []
current = self
while current
chain << current
current = block.call(current)
end
chain
end
end
```
Here's an example use:
```rb
class ListNode
attr_accessor :value, :parent
def initialize(value, parent = nil)
@value = value
@parent = parent
end
def ancestors
chain_of(&:parent).shift
end
end
root = ListNode.new("root")
child1 = ListNode.new("child1", root)
child2 = ListNode.new("child2", child1)
puts child2.ancestors.map(&:value)
# => ["child1", "root"]
```
The examples from the motivation section could be rewritten as:
```rb
breadcrumbs = root.chain_of(&:parent_directory)
hierarchy = employee.chain_of(&:manager)
```
## Considerations
- While I'm including the object by default in the chain, it could be more
intuitive to exclude it. In any case, it's easy to remove or add it with
`shift`/`unshift`.
- On a different note, the method could be named differently (I do like
`chain_of`, though). Some alternatives I've considered are `map_chain`,
`traverse`, and `trace_path`.
- The method assumes that the traversal will finish at some point. If the user
has a cyclic structure, it will loop indefinitely. We could stop looping if we
find the same element twice. I don't think it's worth the extra complexity.
- I'm not sure `Object` is the best place for this method. While it's very
general, I think it gives power to the user to decide how to traverse a chain
like this without having to rely on a specific class. Maybe a mixin
(`Traversable`/`Chainable`) would be more appropriate? Could this fit in
`Enumerable`, somehow?
Of course, I'm open to suggestions and feedback. Thanks for reading!
--
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] 5+ messages in thread
* [ruby-core:118558] [Ruby master Feature#20625] Object#chain_of
2024-07-10 18:56 [ruby-core:118535] [Ruby master Feature#20625] Object#chain_of matheusrich (Matheus Richard) via ruby-core
@ 2024-07-11 10:10 ` zverok (Victor Shepelev) via ruby-core
2024-07-26 19:19 ` [ruby-core:118703] " matheusrich (Matheus Richard) via ruby-core
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: zverok (Victor Shepelev) via ruby-core @ 2024-07-11 10:10 UTC (permalink / raw)
To: ruby-core; +Cc: zverok (Victor Shepelev)
Issue #20625 has been updated by zverok (Victor Shepelev).
```ruby
breadcrumbs = Enumerator.produce(parent, &:parent_directory).take_while { !_1.nil? }
hierarchy = Enumerator.produce(employee, &:member).take_while { !_1.nil? }
```
My initial proposal for what became `Enumerator.produce` (it is the opposite of `Enumerable#reduce`, hence the name) was to make it an Object’s method.
But it was decided that increasing Object’s API is too breaking change, so it became a separate `Enumerator` method... Which, to the best of my estimation, not a lot of people are aware about (because even when somebody looks for “method like this”, it is not obvious where to look for it).
----------------------------------------
Feature #20625: Object#chain_of
https://bugs.ruby-lang.org/issues/20625#change-109081
* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
## Motivation
It's often common to traverse a tree/list-like structure in order to get a chain
of elements. This proposal is to add a method to `Object` that allows collecting
a chain of elements by applying a block to each element. It doesn't require the
root element to be an instance of a specific class or respond to a specific
protocol.
I think this method could be useful in many cases, since hierarchies like this
are common in codebases (e.g. file systems, organizations structures, commit
histories, breadcrumbs in web apps, configuration hierarchies, etc.).
Here are some examples extracted from real codebases (simplified for the sake of
the example/privacy):
```rb
# Given a file system structure, get the breadcrumbs of a file or directory
def breadcrumbs(root)
crumbs = []
current = root
while current
crumbs << current
current = current.parent_dir
end
crumbs
end
# Given an employee, get the hierarchy of managers
def hierarchy(employee)
hierarchy = []
current = employee
while current
hierarchy << current
current = current.manager
end
hierarchy
end
```
## Implementation
The implementation in Ruby could look like this:
```rb
class Object
def chain_of(&block)
chain = []
current = self
while current
chain << current
current = block.call(current)
end
chain
end
end
```
Here's an example use:
```rb
class ListNode
attr_accessor :value, :parent
def initialize(value, parent = nil)
@value = value
@parent = parent
end
def ancestors
chain_of(&:parent).shift
end
end
root = ListNode.new("root")
child1 = ListNode.new("child1", root)
child2 = ListNode.new("child2", child1)
puts child2.ancestors.map(&:value)
# => ["child1", "root"]
```
The examples from the motivation section could be rewritten as:
```rb
breadcrumbs = root.chain_of(&:parent_directory)
hierarchy = employee.chain_of(&:manager)
```
## Considerations
- While I'm including the object by default in the chain, it could be more
intuitive to exclude it. In any case, it's easy to remove or add it with
`shift`/`unshift`.
- On a different note, the method could be named differently (I do like
`chain_of`, though). Some alternatives I've considered are `map_chain`,
`traverse`, and `trace_path`.
- The method assumes that the traversal will finish at some point. If the user
has a cyclic structure, it will loop indefinitely. We could stop looping if we
find the same element twice. I don't think it's worth the extra complexity.
- I'm not sure `Object` is the best place for this method. While it's very
general, I think it gives power to the user to decide how to traverse a chain
like this without having to rely on a specific class. Maybe a mixin
(`Traversable`/`Chainable`) would be more appropriate? Could this fit in
`Enumerable`, somehow?
Of course, I'm open to suggestions and feedback. Thanks for reading!
--
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] 5+ messages in thread
* [ruby-core:118703] [Ruby master Feature#20625] Object#chain_of
2024-07-10 18:56 [ruby-core:118535] [Ruby master Feature#20625] Object#chain_of matheusrich (Matheus Richard) via ruby-core
2024-07-11 10:10 ` [ruby-core:118558] " zverok (Victor Shepelev) via ruby-core
@ 2024-07-26 19:19 ` matheusrich (Matheus Richard) via ruby-core
2024-08-01 9:34 ` [ruby-core:118765] " matz (Yukihiro Matsumoto) via ruby-core
2024-08-24 1:53 ` [ruby-core:118943] " matheusrich (Matheus Richard) via ruby-core
3 siblings, 0 replies; 5+ messages in thread
From: matheusrich (Matheus Richard) via ruby-core @ 2024-07-26 19:19 UTC (permalink / raw)
To: ruby-core; +Cc: matheusrich (Matheus Richard)
Issue #20625 has been updated by matheusrich (Matheus Richard).
@zverok do you think `Enumerator#chain_of?` would be useful as an specialization of `produce().take_while {!_1.nil?}`?
----------------------------------------
Feature #20625: Object#chain_of
https://bugs.ruby-lang.org/issues/20625#change-109240
* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
## Motivation
It's often common to traverse a tree/list-like structure in order to get a chain
of elements. This proposal is to add a method to `Object` that allows collecting
a chain of elements by applying a block to each element. It doesn't require the
root element to be an instance of a specific class or respond to a specific
protocol.
I think this method could be useful in many cases, since hierarchies like this
are common in codebases (e.g. file systems, organizations structures, commit
histories, breadcrumbs in web apps, configuration hierarchies, etc.).
Here are some examples extracted from real codebases (simplified for the sake of
the example/privacy):
```rb
# Given a file system structure, get the breadcrumbs of a file or directory
def breadcrumbs(root)
crumbs = []
current = root
while current
crumbs << current
current = current.parent_dir
end
crumbs
end
# Given an employee, get the hierarchy of managers
def hierarchy(employee)
hierarchy = []
current = employee
while current
hierarchy << current
current = current.manager
end
hierarchy
end
```
## Implementation
The implementation in Ruby could look like this:
```rb
class Object
def chain_of(&block)
chain = []
current = self
while current
chain << current
current = block.call(current)
end
chain
end
end
```
Here's an example use:
```rb
class ListNode
attr_accessor :value, :parent
def initialize(value, parent = nil)
@value = value
@parent = parent
end
def ancestors
chain_of(&:parent).shift
end
end
root = ListNode.new("root")
child1 = ListNode.new("child1", root)
child2 = ListNode.new("child2", child1)
puts child2.ancestors.map(&:value)
# => ["child1", "root"]
```
The examples from the motivation section could be rewritten as:
```rb
breadcrumbs = root.chain_of(&:parent_directory)
hierarchy = employee.chain_of(&:manager)
```
## Considerations
- While I'm including the object by default in the chain, it could be more
intuitive to exclude it. In any case, it's easy to remove or add it with
`shift`/`unshift`.
- On a different note, the method could be named differently (I do like
`chain_of`, though). Some alternatives I've considered are `map_chain`,
`traverse`, and `trace_path`.
- The method assumes that the traversal will finish at some point. If the user
has a cyclic structure, it will loop indefinitely. We could stop looping if we
find the same element twice. I don't think it's worth the extra complexity.
- I'm not sure `Object` is the best place for this method. While it's very
general, I think it gives power to the user to decide how to traverse a chain
like this without having to rely on a specific class. Maybe a mixin
(`Traversable`/`Chainable`) would be more appropriate? Could this fit in
`Enumerable`, somehow?
Of course, I'm open to suggestions and feedback. Thanks for reading!
--
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] 5+ messages in thread
* [ruby-core:118765] [Ruby master Feature#20625] Object#chain_of
2024-07-10 18:56 [ruby-core:118535] [Ruby master Feature#20625] Object#chain_of matheusrich (Matheus Richard) via ruby-core
2024-07-11 10:10 ` [ruby-core:118558] " zverok (Victor Shepelev) via ruby-core
2024-07-26 19:19 ` [ruby-core:118703] " matheusrich (Matheus Richard) via ruby-core
@ 2024-08-01 9:34 ` matz (Yukihiro Matsumoto) via ruby-core
2024-08-24 1:53 ` [ruby-core:118943] " matheusrich (Matheus Richard) via ruby-core
3 siblings, 0 replies; 5+ messages in thread
From: matz (Yukihiro Matsumoto) via ruby-core @ 2024-08-01 9:34 UTC (permalink / raw)
To: ruby-core; +Cc: matz (Yukihiro Matsumoto)
Issue #20625 has been updated by matz (Yukihiro Matsumoto).
I don't agree with `Object#chain_of`. Maybe there's a room for a gem, or enhancing `Enumerable#product`.
Matz.
----------------------------------------
Feature #20625: Object#chain_of
https://bugs.ruby-lang.org/issues/20625#change-109317
* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
## Motivation
It's often common to traverse a tree/list-like structure in order to get a chain
of elements. This proposal is to add a method to `Object` that allows collecting
a chain of elements by applying a block to each element. It doesn't require the
root element to be an instance of a specific class or respond to a specific
protocol.
I think this method could be useful in many cases, since hierarchies like this
are common in codebases (e.g. file systems, organizations structures, commit
histories, breadcrumbs in web apps, configuration hierarchies, etc.).
Here are some examples extracted from real codebases (simplified for the sake of
the example/privacy):
```rb
# Given a file system structure, get the breadcrumbs of a file or directory
def breadcrumbs(root)
crumbs = []
current = root
while current
crumbs << current
current = current.parent_dir
end
crumbs
end
# Given an employee, get the hierarchy of managers
def hierarchy(employee)
hierarchy = []
current = employee
while current
hierarchy << current
current = current.manager
end
hierarchy
end
```
## Implementation
The implementation in Ruby could look like this:
```rb
class Object
def chain_of(&block)
chain = []
current = self
while current
chain << current
current = block.call(current)
end
chain
end
end
```
Here's an example use:
```rb
class ListNode
attr_accessor :value, :parent
def initialize(value, parent = nil)
@value = value
@parent = parent
end
def ancestors
chain_of(&:parent).shift
end
end
root = ListNode.new("root")
child1 = ListNode.new("child1", root)
child2 = ListNode.new("child2", child1)
puts child2.ancestors.map(&:value)
# => ["child1", "root"]
```
The examples from the motivation section could be rewritten as:
```rb
breadcrumbs = root.chain_of(&:parent_directory)
hierarchy = employee.chain_of(&:manager)
```
## Considerations
- While I'm including the object by default in the chain, it could be more
intuitive to exclude it. In any case, it's easy to remove or add it with
`shift`/`unshift`.
- On a different note, the method could be named differently (I do like
`chain_of`, though). Some alternatives I've considered are `map_chain`,
`traverse`, and `trace_path`.
- The method assumes that the traversal will finish at some point. If the user
has a cyclic structure, it will loop indefinitely. We could stop looping if we
find the same element twice. I don't think it's worth the extra complexity.
- I'm not sure `Object` is the best place for this method. While it's very
general, I think it gives power to the user to decide how to traverse a chain
like this without having to rely on a specific class. Maybe a mixin
(`Traversable`/`Chainable`) would be more appropriate? Could this fit in
`Enumerable`, somehow?
Of course, I'm open to suggestions and feedback. Thanks for reading!
--
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] 5+ messages in thread
* [ruby-core:118943] [Ruby master Feature#20625] Object#chain_of
2024-07-10 18:56 [ruby-core:118535] [Ruby master Feature#20625] Object#chain_of matheusrich (Matheus Richard) via ruby-core
` (2 preceding siblings ...)
2024-08-01 9:34 ` [ruby-core:118765] " matz (Yukihiro Matsumoto) via ruby-core
@ 2024-08-24 1:53 ` matheusrich (Matheus Richard) via ruby-core
3 siblings, 0 replies; 5+ messages in thread
From: matheusrich (Matheus Richard) via ruby-core @ 2024-08-24 1:53 UTC (permalink / raw)
To: ruby-core; +Cc: matheusrich (Matheus Richard)
Issue #20625 has been updated by matheusrich (Matheus Richard).
@matz since we don't have a `not_nil?` method, what do you think about adding a `take_until` method to Enumerable or Enumerator? That would make things easier to write/read
```ruby
Enumerator.produce(parent, &:parent_directory).take_until(&:nil?)
```
----------------------------------------
Feature #20625: Object#chain_of
https://bugs.ruby-lang.org/issues/20625#change-109514
* Author: matheusrich (Matheus Richard)
* Status: Open
----------------------------------------
## Motivation
It's often common to traverse a tree/list-like structure in order to get a chain
of elements. This proposal is to add a method to `Object` that allows collecting
a chain of elements by applying a block to each element. It doesn't require the
root element to be an instance of a specific class or respond to a specific
protocol.
I think this method could be useful in many cases, since hierarchies like this
are common in codebases (e.g. file systems, organizations structures, commit
histories, breadcrumbs in web apps, configuration hierarchies, etc.).
Here are some examples extracted from real codebases (simplified for the sake of
the example/privacy):
```rb
# Given a file system structure, get the breadcrumbs of a file or directory
def breadcrumbs(root)
crumbs = []
current = root
while current
crumbs << current
current = current.parent_dir
end
crumbs
end
# Given an employee, get the hierarchy of managers
def hierarchy(employee)
hierarchy = []
current = employee
while current
hierarchy << current
current = current.manager
end
hierarchy
end
```
## Implementation
The implementation in Ruby could look like this:
```rb
class Object
def chain_of(&block)
chain = []
current = self
while current
chain << current
current = block.call(current)
end
chain
end
end
```
Here's an example use:
```rb
class ListNode
attr_accessor :value, :parent
def initialize(value, parent = nil)
@value = value
@parent = parent
end
def ancestors
chain_of(&:parent).shift
end
end
root = ListNode.new("root")
child1 = ListNode.new("child1", root)
child2 = ListNode.new("child2", child1)
puts child2.ancestors.map(&:value)
# => ["child1", "root"]
```
The examples from the motivation section could be rewritten as:
```rb
breadcrumbs = root.chain_of(&:parent_directory)
hierarchy = employee.chain_of(&:manager)
```
## Considerations
- While I'm including the object by default in the chain, it could be more
intuitive to exclude it. In any case, it's easy to remove or add it with
`shift`/`unshift`.
- On a different note, the method could be named differently (I do like
`chain_of`, though). Some alternatives I've considered are `map_chain`,
`traverse`, and `trace_path`.
- The method assumes that the traversal will finish at some point. If the user
has a cyclic structure, it will loop indefinitely. We could stop looping if we
find the same element twice. I don't think it's worth the extra complexity.
- I'm not sure `Object` is the best place for this method. While it's very
general, I think it gives power to the user to decide how to traverse a chain
like this without having to rely on a specific class. Maybe a mixin
(`Traversable`/`Chainable`) would be more appropriate? Could this fit in
`Enumerable`, somehow?
Of course, I'm open to suggestions and feedback. Thanks for reading!
--
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] 5+ messages in thread
end of thread, other threads:[~2024-08-24 1:53 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-07-10 18:56 [ruby-core:118535] [Ruby master Feature#20625] Object#chain_of matheusrich (Matheus Richard) via ruby-core
2024-07-11 10:10 ` [ruby-core:118558] " zverok (Victor Shepelev) via ruby-core
2024-07-26 19:19 ` [ruby-core:118703] " matheusrich (Matheus Richard) via ruby-core
2024-08-01 9:34 ` [ruby-core:118765] " matz (Yukihiro Matsumoto) via ruby-core
2024-08-24 1:53 ` [ruby-core:118943] " matheusrich (Matheus Richard) 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).