Last active
December 10, 2015 16:28
-
-
Save NIA/4461055 to your computer and use it in GitHub Desktop.
Very laconic functional-style working with collections in Scala
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
// Example 1: join two collections of filenames, find existing ones and load them | |
(configFiles ++ extraConfigFiles) map { new File(_) } filter { _.canRead } foreach load | |
// Example 2: invoke `prompt` function in an infinite loop, transform the result with trim method, filter out non-empty and process the others | |
Iterator continually(prompt) map { _.trim } filterNot { _.isEmpty } foreach { input => ... } |
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
# Example 1 in Ruby: almost the same, but we always have to waste some characters each time for lambda parameter definition | |
# and we cannot leave out `.` or simply pass `load` as argument to each without wraping it into block | |
(config_files + extra_config_files).find_all {|s| File.exists? s}.map {|s| File.new s }.each {|f| load(f)} | |
# The 2nd Scala example is simply inavailable in idiomatic Ruby. | |
# While we can emulate Scala's Iterator.continually with Enumerator.new (Ruby 1.9 feature), | |
# the following 'naive' approach will NOT work :( | |
Enumerator.new{|y| loop{ y << prompt } }.map(&:strip).reject(&:empty?).each{|input| ... } | |
# It is almost as short as Scala's, but... last block will never be invoked. | |
# That's because Enumerable#map and Enumerable#reject are not lazy and return arrays instead of Enumerator. | |
# So the very first map will not return until the Enumerator ends (that is, never) | |
# | |
# As the last resort we can patch Enumerator class as suggested there: http://en.wikibooks.org/wiki/Ruby_Programming/Reference/Objects/Enumerable#Lazy_evaluation | |
class Enumerator | |
def defer(&blk) | |
self.class.new do |y| | |
each do |*input| | |
blk.call(y, *input) | |
end | |
end | |
end | |
# And also add some shortcuts | |
def lazy_map | |
defer { |out, inp| out.yield yield(inp) } | |
end | |
def lazy_reject | |
defer { |out, inp| out.yield inp unless yield(inp) } | |
end | |
def self.continually(&blk) | |
self.new do |y| | |
loop { y << blk.call } | |
end | |
end | |
end | |
# So we can finally write | |
Enumerator.continually { prompt }.lazy_map(&:strip).lazy_reject(&:empty?).each{|input| ... } | |
# Exactly like in Scala! But for what cost... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment