Skip to content

Instantly share code, notes, and snippets.

@havenwood
Created July 14, 2026 17:43
Show Gist options
  • Select an option

  • Save havenwood/0103fbfb671fde7646ab10ede267361b to your computer and use it in GitHub Desktop.

Select an option

Save havenwood/0103fbfb671fde7646ab10ede267361b to your computer and use it in GitHub Desktop.
An example pattern matching mixin for data objects
require_relative "pattern_matchable"
class Color
extend PatternMatchable
array_pattern :red, :green, :blue
hash_pattern :red, :green, :blue,
hex: -> { format "#%06x", packed },
brightness: -> { (red * 299 + green * 587 + blue * 114).fdiv(1_000) }
def initialize(packed)
@packed = packed
freeze
end
def red = packed >> 16
def green = packed >> 8 & 0xff
def blue = packed & 0xff
private
attr_reader :packed
end
color = Color.new(0x663399)
case color
in Color[102, green, blue]
puts "array pattern → rgb(102, #{green}, #{blue})"
end
case color
in Color[hex: "#663399", brightness: 0...128 => brightness]
puts "hash pattern → #663399, brightness #{brightness.round(1)}"
end
palette = [Color.new(0xffd700), color, Color.new(0x00ff00)]
case palette
in [*, Color[brightness: 0...128, hex:] => dark_color, *]
puts "find pattern → #{hex}, red #{dark_color.red}"
end
module PatternMatchable
def array_pattern(*accessors)
validate_accessors accessors
define_method :deconstruct do
accessors.map { public_send it }
end
end
def hash_pattern(*accessors, **derivations)
validate_accessors accessors
validate_derivations derivations
extractors = accessors
.to_h { [it, it] }
.merge(derivations)
.freeze
define_method :deconstruct_keys do |keys|
fields = keys ? extractors.slice(*keys) : extractors.dup
fields.transform_values! do
case it
in Symbol => accessor
public_send accessor
in Proc => derivation
instance_exec(&derivation)
end
end
end
end
private
def validate_accessors(accessors)
accessors.each do
next if it.is_a?(Symbol)
raise TypeError, "wrong pattern accessor type `#{it.class}` (expected `Symbol`)"
end
end
def validate_derivations(derivations)
derivations.each do
next if _2.is_a?(Proc)
raise TypeError, "wrong pattern derivation type `#{_2.class}` for `#{_1.inspect}` (expected `Proc`)"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment