of specifying patterns to which some data should conform and then checking to see if it does and deconstructing the data according to those patterns" • Learn You a Haskell for Great Good! (Miran Lipovaca) • For Rubyists • case/when + multiple assignment
may arise you handle a JSON data { "name": "Alice", "age": 30, "children": [ { "name": "Bob", "age": 2 } ] } case JSON.parse(json, symbolize_names: true) in {name: "Alice", children: [{name: "Bob", age: age}]} p age # => 2 end
first one finds a match • If no pattern matches, the else clause is executed • If no pattern matches emerge and there is no else clause, the NoMatchingPatternError exception is raised case expr in pattern [if|unless condition] ... in pattern [if|unless condition] ... else ... end
been introduced • If the pattern matches, it returns true • Otherwise, it returns false # Syntax expr in pattern # e.g. if [0, 1] in [0, a] p a #=> 1 end
an existing variable's value, use ^ • It is same as the pin operator in Elixir a = 0 case 1 in ^a # means `in 0` :unreachable end #=> NoMatchingPatternError
solely for array objects • Steps to matching: 1.The pattern and the object are compared by Constant === object 2.If it returns true, check if the object has a #deconstruct method that returns Array 3.If the object has a #deconstruct method, its return value is used to perform sub-pattern matching pat: Constant(pat, ..., *var, pat, ...) | Constant[pat, ..., *var, pat, ...] | [pat, ..., *var, pat, ...] # Syntactic sugar for BasicObject(...)
objects • Steps to matching: 1.The pattern and the object are compared by Constant === object 2.If it returns true, check if the object has a #deconstruct_keys method that returns Hash 3.If the object has a #deconstruct_keys method, its return value is used to perform sub-pattern matching pat: Constant(id: pat, id:, ..., **var) | Constant[id: pat, id:, ..., **var] | {id:, id: pat, **var} # Syntactic sugar for BasicObject(...)
def deconstruct_keys(keys) self end end case {a: 0, b: 1} in Hash(a: a, b: 1) in Object[a: a] in {a: a, **rest} p rest #=> {b: 1} in {a: a, **nil} :unreachable end
implementation • If we implement deconstruct_keys imprudently, the results might be very inefficient class Time def deconstruct_keys(keys) { year: year, month: month, asctime: asctime, ctime: ctime, ..., yday: yday, zone: zone } end end case Time.now in year: p year #=> 2019 end
specified in the pattern • You can ignore any keys that are not included in keys • If **rest is specified in the pattern, nil is passed instead • In such case, you must return all key-value pairs class Time VALID_KEYS = %i(year month ...) def deconstruct_keys(keys) if keys (VALID_KEYS & keys).each_with_object({}) do |k, h| h[k] = send(k) end else {year: year, month: month, ...} end end end now = Time.now case now in year: # Calls now.deconstruct_keys([:year]), # receives {year: 2019} end
introduces the following changes to Ruby • Syntax • case/in • Namespace • NoMatchingPatternError • deconstruct, deconstruct_keys • We should be careful about their effects
NoMatchingPatternError • If NoMatchingPatternError has already been used, it will not work • deconstruct, deconstruct_keys • No effect will result if pattern matching is not used • Still, it is preferable to choose a name that is not currently used
in statically typed functional programming languages • Ruby is a dynamically typed object oriented language • It is necessary to combine these essences efficiently • Powerful Array, Hash support • Encourage duck typing
are very important data structures for Ruby • e.g. multiple assignment, keyword arguments • Provide array patterns, hash patterns • Brackets and braces are optional • id: : syntactic sugar for id: id
to write code to check that the object is an instance of a specific class • Provide concise syntax for duck typing case time in year:, month: ... in Time(year:, month:) ... end
to write code to check that the object is an instance of a specific class • Do not provide a way to make a match with arguments # Syntax Error def m(a, b) in Integer, String ... end
deconstruct_keys for builtin/standard library • Few classes have deconstruct/deconstruct_keys now (Array, Hash, Struct) • Improve performance • Extend the pin operator(?) • Revise scope(?) • Allow Non-symbol keys for a hash pattern(?)