Idiosyncratic Ruby

Quiz


@JanLelisJanuary 2020𝕽𝖀𝕲::𝕭

idiosyncratic-ruby.com

Documenting lesser-known features in Ruby


We've got a match!

Pattern Matching

= super fancy case statements


…implemented by Kazuki Tsujimoto (k-tsj)


…in the makes since 2012 (pattern-match gem)

Warning

Pattern matching is experimental, and the behavior may change in future versions of Ruby!


Deactivate with
Warning[:experimental] = false


Or on CLI
$ ruby -W:no-experimental

Old case Statement

case forty_two
when 42
  puts "found number 42"
when /42/
  puts "matched 42"
else
  puts "no 42"
end

New case Statement

case forty_two
in 42
  puts "found number 42"
in /42/
  puts "matched 42"
else
  puts "no 42"
end

THE SAME!!!

casein

in is no stranger to us…

Warm-up question:
Where else is it used in the Ruby language


for n in [2,3,4] do puts n end

instyle case statement observations


uses === method for checking a condition

no mix and match between in and when

"empty" case statements don't work with in

without else clause, one condition must match or NoMatchingPatternError

Is this code valid syntax?

case forty_two
in 42 if false
  puts "found number 42"
in /42/ unless forty_two.length <3
  puts "matched 42"
else
  puts "no answer"
end

Syntax OK

Is this Ruby code?

def ask
  0b101010 in answer
  answer
end

ask #=> 42

Matching assign variables!

Even without a surrounding case-statement!

Is this Ruby or pseudo-code?

[1, 2, 3, 4] in [first, second, *other]

Syntax OK


Think: Put [1, 2, 3, 4] into [first, second, *other]

{
  verb: "CREATE",
  endpoint: "/feierabend",
  authed: true,
} in { verb:, endpoint: }

verb #=> "CREATE"
endpoint #=> "/feierabend"

What will happen here?

[[1, 2, 3, 4], [:a, :b]] in [[n1, n2], symbols]

NoMatchingPatternError

[[1, 2, 3, 4], [:a, :b]] in [[n1, n2, *other], symbols]

What will happen here?

case [1, 2, 2, 3, 4]
in [1, 2, 2, a, 4] if false
  p :first
in [1, b, b, Integer, Integer]
  p :second
else
  p :third
end

SyntaxError (duplicated variable name)

_ instead of b will work

What will happen here?

case [1, 2, 2, 3, 4]
in [1, 2, 2, a, 4] if false
  p :first
in [1, b, c, Integer, Integer]
  p [:second, b]
else
  p :third
end

# => [:second, 2]


…and what's the value of a?


a #=> 4

What's going on here?

home = /Berlin|Potsdam/
case {
  cities: ["Berlin", "Porto", "Tokio"],
  groups: [["Andrea", "Kim"], ["Chris", "Sascha"]],
  reasons: ["Unknown", "Meaningfulness"],
}
in { cities: [^home, *other_cities],
     groups: [[a, b] => first, *],
     reasons: [important] }
  p [:lets_go_to?, other_cities, first]
in { cities: }
  p [:listing_all, cities]
end

[:listing_all, ["Berlin", "Porto", "Tokio"]]

What's going on here?

→ ??? ←

case [1,2,3]
in Array(a,b,c)
  p [:first, a, b, c]
in Array[a,b,c]
  p [:second, a, b, c]
in [a,b,c]
  p [:third, a, b, c]
end

#=> [?, 3, 2, 1]
class Array
  def deconstruct
    reverse
  end
end

More about pattern matching

speakerdeck.com/k_tsj/pattern-matching-new-feature-in-ruby-2-dot-7

RANDOM 2.7 NEWS

How to produce this output?


[1, 2, 3, 4].__________ { |n| n.even? ? n/2 : nil } #=> [1, 2]

RANDOM 2.7 NEWS

How to produce this output?


[1, 2, 3, 4].filter_map { |n| n.even? ? n/2 : nil } #=> [1, 2]

How to code golf this?

[1, 2, 3, 4].filter_map { _1.even? ? _1/2 : nil } [1,2,3,4].filter_map{_1%2<1&&_1/2}

RANDOM 2.7 NEWS

What will happen?


send def no ** nil; end, nil: :no


ArgumentError (no keywords accepted)

MORE 2.7 NEWS

Commentary by Koichi Sasada and Yusuke Endoh sourcediving.com/ruby-2-7-news-commentary-by-cookpads-full-time-ruby-comitters-bdbaacb36d0c

Hyperexpressivism

You see . (a dot) in some Ruby code

How many different meanings can it have?

What's the meaning of .? (1)


1.42


(floating point number)

What's the meaning of .? (2)


object.method


(method call)

What's the meaning of .? (2-3)


object.()


(method call to a method named "call")

What's the meaning of .? (2-4)


def object.method() end


(method definition)

What's the meaning of .? (3-5)


"."


(part of string literal)

What's the meaning of .? (4-6)


%.string.


(string delimiter)

What's the meaning of .? (5-7)


:"."


(part of symbol name)

What's the meaning of .? (6-8)


/./


(regex literal, match-any)

What's the meaning of .? (6-9)


/\./


(regex literal, escaped)

What's the meaning of .? (6-10)


#.


(part of comment)

What's the meaning of .? (7-11)


1..42


(range, inclusive)

What's the meaning of .? (7-12)


1...42


(range, exclusive)

What's the meaning of .? (7-14)


1..
1...


(endless range, inclusive + exclusive, new in 2.6)

What's the meaning of .? (7-16)


..42
...42


("beginless" range, inclusive + exclusive, new in 2.7)

What's the meaning of .? (7-18)


p...;
p..;


(nilistic range, inclusive + exclusive, new in 2.7)

What's the meaning of .? (8-19)


def puts!(...) puts(...) puts "!!!" end putz "hey" #=> hey !!!


(argument forwarding, new in 2.7)

What's the meaning of .? (9-20)

BONUS ROUND


$.


(part of this special variable name, which returns line number of input read from $STDIN)

Thank you!

idiosyncratic-ruby.com/quiz

@JanLelis