Top Banner
Threequals Ruby's greatest operator ===
68

Threequals - Case Equality in Ruby

Jun 14, 2015

Download

Software

Louis Scoras

There are many things that make Ruby a great language, but above all else, the beautiful and friendly syntax. A perfect exemplar of this is the case statement: case enables a flexible method of dispatching on an object that is both natural and intuitive. But case can't do it alone. No, it requires the help of it's little-known and under-appreciated sidekick the === (threequals) operator.


In this talk we'll dive into this fascinating corner of the Ruby language and see what trouble we can cause with the humble threequals. We'll go over the basics of how it interacts with case, and then go into some tips and tricks for making the most of this useful bit of syntax, and ultimately create a little pattern matching mini-language as a demonstration.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Threequals - Case Equality in Ruby

Threequals

Ruby's greatest operator===

Page 2: Threequals - Case Equality in Ruby

Me

===

Page 3: Threequals - Case Equality in Ruby

/Lou(?: Scoras)/ === nameemail === "[email protected]"codes === :"github.com/ljsc"

10..(1.0/0) === ruby.years_codingworks === "Econify.com"

Me

Page 4: Threequals - Case Equality in Ruby

The equals sign

===

Page 5: Threequals - Case Equality in Ruby

What does it mean in ruby?

The equals sign

Page 6: Threequals - Case Equality in Ruby

It depends on how

The equals sign

loudly youYELL.

Page 7: Threequals - Case Equality in Ruby

=(Whispered.)

Page 8: Threequals - Case Equality in Ruby

==(Inside voice.)

Page 9: Threequals - Case Equality in Ruby

===(IRC SHOUT!)

Page 10: Threequals - Case Equality in Ruby
Page 11: Threequals - Case Equality in Ruby

Case Equality

===

Page 12: Threequals - Case Equality in Ruby

Case Equality

Use case equality whenever you need to discriminate between one or more cases.

Page 13: Threequals - Case Equality in Ruby

Case Equality

This operator and the case statement go hand in hand.

Page 14: Threequals - Case Equality in Ruby

Case Equality

x = gets.strip.to_i

case xwhen 1 then "one"when 2 then "two"when 3 then "three"end

Page 15: Threequals - Case Equality in Ruby

Case Equality

x = gets.strip.to_i

case xwhen 1 then "one"when 2 then "two"when 3 then "three"end

if 1 === x "one"elsif 2 === x "two"elsif 3 === x "three"end

Page 16: Threequals - Case Equality in Ruby

Case Equality

case x when 1 then "one"

if 1 === x

Page 17: Threequals - Case Equality in Ruby

Case Equality

Case equality is not symmetric in general.

Page 18: Threequals - Case Equality in Ruby

Case Equality

pattern target===

Page 19: Threequals - Case Equality in Ruby

Case Equality

if 1 === x "easy"elsif 2 === x || 3 === x "struggling"else "Why do you make me work so hard?"end

case xwhen 1 then "easy"when 2, 3 "struggling"else "Why do you make me work so hard?"end

Page 20: Threequals - Case Equality in Ruby

The Default

===

Page 21: Threequals - Case Equality in Ruby

The Default

"For class Object, effectively the same as calling ==, but typically overridden by descendants to provide meaningful semantics in case statements."

Page 22: Threequals - Case Equality in Ruby

Useful Builtins

===

Page 23: Threequals - Case Equality in Ruby

Useful Builtins

Class Semantics Notes

Object ==

Regex match Returns a boolean

Range include?

Fixnum/Bignum/Float == Coerces numeric types

Class (Module) is_a? On an instance

Proc call

Page 24: Threequals - Case Equality in Ruby

Predicate Objects

===

Page 25: Threequals - Case Equality in Ruby

Predicate Objects

Predicates are functions that return a boolean value.

Page 26: Threequals - Case Equality in Ruby

Predicate Objects

Create objects to encapsulate case equality operations.

Page 27: Threequals - Case Equality in Ruby

Predicate Objects

What is your quest?

Page 28: Threequals - Case Equality in Ruby

Predicate Objects

class Proceed def ===(knight) knight.favorite_color == "blue" endend

Page 29: Threequals - Case Equality in Ruby

Predicate Objects

class Doomed def ===(knight) knight.knows_capital?("Assyria") endend

Page 30: Threequals - Case Equality in Ruby

Predicate Objects

def cross_bridge!(knight) case knight when Doomed.new then "ARGGGH!!" when Proceed.new then "Right, off you go" endend

Page 31: Threequals - Case Equality in Ruby

Parameterize!

===

Page 32: Threequals - Case Equality in Ruby

Parameterize!

class LikesColor def initialize(color) @color = color end def ===(knight) knight.favorite_color == @color endend

Page 33: Threequals - Case Equality in Ruby

Parameterize!

def cross_bridge!(knight) case knight when LikesColor.new("Yellow") "ARGGGH!!" when LikesColor.new("Blue") "Right, off you go" endend

Page 34: Threequals - Case Equality in Ruby

Composites

===

Page 35: Threequals - Case Equality in Ruby

Composite Pattern

"The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object."

http://en.wikipedia.org/wiki/Composite_pattern

Page 36: Threequals - Case Equality in Ruby

Composite Pattern

UML (purposefully) omitted.

Page 37: Threequals - Case Equality in Ruby

Composite Pattern

Page 38: Threequals - Case Equality in Ruby

Composite Pattern

The "has-a" is not the important part!

Page 39: Threequals - Case Equality in Ruby

Composite Pattern

The "has-a" is not the important part!

Not limited to things that are obviously trees.

Page 40: Threequals - Case Equality in Ruby

Composite Pattern

The most important idea in programming (™): Closure

Page 41: Threequals - Case Equality in Ruby

Composite Pattern

(Not the lexical kind)

The most important idea in programming (™): Closure

Page 42: Threequals - Case Equality in Ruby

Closure Property

Closed operation: Takes one or more object of type T and returns something of type T.

f : Tn -> T

Page 43: Threequals - Case Equality in Ruby

The BridgePt.2

===

Page 44: Threequals - Case Equality in Ruby

The Bridge pt.2

class Or def initialize(a, b) @a, @b = a, b end def ===(other) @a === other || @b === other endend

Page 45: Threequals - Case Equality in Ruby

The Bridge pt.2

class Or def initialize(a, b) @a, @b = a, b end def ===(other) @a === other || @b === other endend

The magic part

Page 46: Threequals - Case Equality in Ruby

The Bridge Pt.2

def cross_bridge!(knight) case knight when Or.new(KnightNamed.new("Lancelot"), KnightNamed.new("Arthur") "Right, off you go" when WearingColor.new("Green") "Kind of arbitrary isn....ARGGGH!!" endend

Page 47: Threequals - Case Equality in Ruby

What a Mouthful

===

Page 48: Threequals - Case Equality in Ruby

What a Mouthful

Page 49: Threequals - Case Equality in Ruby

What a Mouthful

This is all very nice, but it's kind of verbose.

Page 50: Threequals - Case Equality in Ruby

What a Mouthful

This might be a good time for a very thin "DSL" layer.

Page 51: Threequals - Case Equality in Ruby

What a Mouthful

module Threequals::DSL def _and(a,b); And.new(a,b) end def _or(a,b); Or.new(a,b) end def named(a); KnightNamed.new(a) end def doomed(a); Doomed.new(a) endend

Page 52: Threequals - Case Equality in Ruby

The Bridge Pt.3

===

Page 53: Threequals - Case Equality in Ruby

The Bridge Pt.3

def cross_bridge!(knight) extend Threequals::DSL case knight when _or(named("Lancelot"), named("Arthur")) "Right, off you go" when wears "Green" "Kind of arbitrary isn....ARGGGH!!" endend

Page 54: Threequals - Case Equality in Ruby

On Duck Typing

===

Page 55: Threequals - Case Equality in Ruby

On Duck Typing

Why does this work so well for ===?

Page 56: Threequals - Case Equality in Ruby

On Duck Typing

Duck types work best when:

Page 57: Threequals - Case Equality in Ruby

On Duck Typing

Duck types work best when:● The operation(s) are abstract.

Page 58: Threequals - Case Equality in Ruby

On Duck Typing

Duck types work best when:● The operation(s) are abstract.● The interface is granular.

Page 59: Threequals - Case Equality in Ruby

On Duck Typing

Ruby should code should steal this part of functional programming.

Page 60: Threequals - Case Equality in Ruby

On Duck Typing

Ruby should code should steal this part of functional programming.

Not "Proc Soup".

Page 61: Threequals - Case Equality in Ruby

Wrapping Up

===

Page 62: Threequals - Case Equality in Ruby

Wrapping up

We should steal functional ideas, but make them idiomatic

Page 63: Threequals - Case Equality in Ruby

Wrapping up

We should steal functional ideas, but make them idiomatic (we created a poormans pattern matching).

Page 64: Threequals - Case Equality in Ruby

Wrapping up

We should steal functional ideas, but make them idiomatic (we created a poormans pattern matching).

Threequals/case is awesome. Use it.

Page 65: Threequals - Case Equality in Ruby

Wrapping up

We should steal functional ideas, but make them idiomatic (we created a poormans pattern matching).

Threequals/case is awesome. Use it.

Composition is king!

Page 66: Threequals - Case Equality in Ruby

Thanks!

===

Page 67: Threequals - Case Equality in Ruby

Questions?

===

Page 68: Threequals - Case Equality in Ruby

Image credits

"Loud" John Watson. https://flic.kr/p/UfVK"Bridge Climb" Alan. https://flic.kr/p/acyZPw"Compound of five small stellated dodecahedra"

fdecomite. https://flic.kr/p/9ZfhWf"peekaboo" ankakay. https://flic.kr/p/6TSAPW