LaunchKit
All quizzes
Ruby 0 views

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

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.

Only used to confirm and publish your comment. Never shown publicly, never shared.

Markdown: **bold**, `code`, ```fenced blocks```, > quotes, [links](url). HTML and images are not rendered.