Composing procs with << and >>
Question
What is the return value of the following Ruby code?
dec = ->(n) {n-1}
inc = ->(n) {n+1}
dbl = ->(n) {n*2}
(dec << dbl >> dbl << inc).call(0) # => ???
The correct answer is
-
-3 -
It raises
SyntaxError -
3 -
2Correct
Explanation
TL;DR
Proc#<< and Proc#>> compose procs into a new proc. f << g runs g first and feeds its result to f; f >> g runs f first. Both operators have the same precedence and associate left, so the chain parses as ((dec << dbl) >> dbl) << inc and runs inc, dbl, dec, dbl on 0, producing 2.
Composition basics
increment = proc { |x| x + 1 }
double = proc { |x| x * 2 }
(double << increment).call(5) # => 12
(double >> increment).call(5) # => 11
double << increment means "increment, then double": (5 + 1) * 2. double >> increment reads like a pipeline, "double, then increment": (5 * 2) + 1.
Step by step
The operators associate left, so the quiz chain is:
dec = ->(n) { n - 1 }
inc = ->(n) { n + 1 }
dbl = ->(n) { n * 2 }
composed = ((dec << dbl) >> dbl) << inc
composed.call(0) # => 2
The outermost composition is (...) << inc, so inc runs first. Its result flows into dec << dbl, which runs dbl before dec, and the trailing >> dbl runs last:
dec = ->(n) { n - 1 }
inc = ->(n) { n + 1 }
dbl = ->(n) { n * 2 }
x = 0
x = inc.call(x) # => 1
x = dbl.call(x) # => 2
x = dec.call(x) # => 1
x = dbl.call(x) # => 2
To see the order without arithmetic, use procs that print:
one = ->(n) { puts 1 }
two = ->(n) { puts 2 }
three = ->(n) { puts 3 }
four = ->(n) { puts 4 }
(three << two >> four << one).call(nil)
Output:
1
2
3
4
Edge cases
Composition is not limited to procs. Method objects implement << and >> as well, and the argument only needs to respond to call:
add_one = 1.method(:+)
(add_one >> add_one).call(40) # => 42
Share this quiz
Comments
No comments yet. Be the first.