Created
August 10, 2014 09:17
-
-
Save hannestyden/3065a455f3d663313cce to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# # Adventures in `flat_map` land | |
# | |
# Investigating the conceptual difference between Ruby's `flat_map` and Scala's `flatMap`. | |
# | |
# Ruby's `flat_map` | |
a = [[1], [2], [[3]], [[4], 5], [[[6]]]] | |
a.flat_map { |e| e.first } # => [1, 2, 3, 4, [6]] | |
# ... which is the same as ... | |
a.map { |e| e.first }.flatten(1) # => [1, 2, 3, 4, [6]] | |
# Kinda meh, since it's just the combo of existing methods ... | |
# | |
# ... but what about Scala's `flatMap`? | |
# As explained in http://www.brunton-spall.co.uk/post/2011/12/02/map-map-and-flatmap-in-scala/ | |
# the power lies in the elements' respective result of being "flattened" | |
module ArrayFlatMap | |
def flatMap(&block) # urghCase, to avoid collision | |
map(&block). | |
map(&:to_a). # `to_a` is chosen as the "preparation" for the element to be flattened | |
flatten(1) | |
end | |
end | |
class Array; include ArrayFlatMap; end | |
# Each "flattenable" object defines how they "prepare" for being flattened. | |
class Nothing | |
def to_a; []; end | |
end | |
class Something | |
def initialize(value); @value = value; end | |
def to_a; [@value]; end | |
end | |
b = (1..5).to_a | |
b.flatMap { |i| i > 2 ? Something.new(i) : Nothing.new } | |
# => [3, 4, 5] | |
# ... which is of course the same as ... | |
b.select { |i| i > 2 } | |
# => [3, 4, 5] | |
# ... or even ... | |
b.map { |i| i > 2 ? i : nil }.compact | |
# => [3, 4, 5] | |
# ... but the two latter examples can't let the elements decide what flattening entails. | |
# In a ducktyped environment, a more pragmatic way would be to check if each | |
# element is "arrayable", otherwise the element is kept as is. | |
class ResponseOrSelf | |
def initialize(object); @object = object; end | |
def method_missing(method, *arguments, &block) | |
return @object unless @object.respond_to?(method) | |
@object.send(method, *arguments, &block) | |
end | |
end | |
class DucksInOrder | |
def initialize(array); @array = array; end | |
def flat_map(&block) | |
@array.map(&block).map { |e| ResponseOrSelf.new(e).to_a }.flatten(1) | |
end | |
end | |
c = DucksInOrder.new((1..5).to_a) | |
c.flat_map { |i| i > 2 ? Something.new(i) : Nothing.new } | |
# => [3, 4, 5] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment