Mf — Abusing Ruby’s Operator Precedence

Brandon Weaver
Dec 26, 2018 · 4 min read

You may have seen Ruby 2.6’s Proc composition, but did you know there are far more operators you can abuse using Ruby’s proc coercion ( & )? Get ready for another wild ride!

Image for post
Image for post

Proc Coercion

You may have seen this in the shorthand syntax for map, but never really understood what it was doing:

[1, 2, 3].select(&:even?)

The above code selects all even numbers, and expands to be effectively the same as this code:

[1, 2, 3].select { |n| n.even? }

The question becomes how does that work? Well the ampersand in Ruby coerces an object into a proc, effectively the same as saying this:

is_even = :even?.to_proc
=> #<Proc:0x00007ff8cd0c1570(&:even?)>

…which can be called much the same as any other proc:
=> true

That brings us to the next subject, composition.

Proc Composition

add_one = -> x { x + 1 }
multiply_by_five = -> x { x * 5 }
do_both = add_one <<
=> 31
[1, 2, 3].map(&do_both)
=> [6, 11, 16]

Though here’s the interesting thing, operators happen to apply before the ampersand coerces something to a proc for Ruby. This means you can do the following:

[1, 2, 3].map(&add_one << multiply_by_five)
=> [6, 11, 16]

Note that you can use either << or >> for composition, the other just changes the “direction” it gets composed in.

Now that’s interesting. If it works with the shovel operator, who’s to say it doesn’t work with other operators as well.

Before we get into that though, let’s take a quick look into closures as a refresher.


adds = -> x { -> y { x + y } }
=> #<Proc:0x00007fca97980df0@(pry):9 (lambda)>
adds_one =
=> #<Proc:0x00007fca979f26d0@(pry):9 (lambda)>
=> 3

Our adds function is returning another function, and that function remembers the context it was defined in. In simpler terms, it still remembers what x was from when it was created which was 1.

We could use it inline as well if we wanted to:

[1, 2, 3].map(&
=> [6,7,8]

What does that have to do with anything though? Well operators have a fun little property to them in Ruby, and remember that shovel<< is an operator.

Operators are Methods

=> 4

Ruby just makes that go away magically at run time using some magic to make it nicer to type out. Granted that’s a gross simplification, but it will do for the purposes of this article.

Given that they’re methods, that means we can define our own classes with operators:

class Foo
attr_reader :value
def initialize(value)
@value = value
def +(other) + self.value)
end +
=> #<Foo:0x00007fca978ffe30 @value=15>

Now that’s interesting, but what if we took it one step further? Remember closures from earlier? Oh yes.

The Emergence of Mf

List(1, 2, 3).map(_ + 5)

Nice, succinct, and really danged handy to have. In other words I really wanted it in Ruby, and as one does I decided to use some of the tools above to make a new idea: Mf (Modifier Functions).

Let’s take a look into a basic variant of it:

module Mf
def self.+(value)
-> other_value { value + other_value }
add_one = Mf + 1
=> #<Proc:0x00007fca979b2800@(pry):52 (lambda)>
=> 3

Remembering from earlier, that means we can inline it like so:

[1, 2, 3].map(&Mf + 5)
=> [6, 7, 8]

Fast forward to defining all the operators and you can imagine where it went from here.

Do remember though that [] is also a method, which means for all those pesky JSON blobs you can hash out one level like so:['name'])

Wrapping Up

But what if you wanted to do multiple levels? Well that’s a story for another day and a good deal of madness that would make this a very long article indeed.

In the mean time, meditate on the fact that & coerces things into procs, and one could intercept any operator called on an object and keep track of all of them, waiting for that eventual proc coercion.

Sound confusing? Great! That means it’ll be an especially fun article to read later.

We call those Sf, or Stack Functions, and they’re all types of extra fun. Give it a read!

In the mean time, happy holidays and happy hacking!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store