Created
July 14, 2026 17:43
-
-
Save havenwood/0103fbfb671fde7646ab10ede267361b to your computer and use it in GitHub Desktop.
An example pattern matching mixin for data objects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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