Last active
December 17, 2023 22:54
-
-
Save alessandro-fazzi/79b59186e2f81153f94a7952dbe4ceb2 to your computer and use it in GitHub Desktop.
[Study] Custom matchers for pattern matching
This file contains 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
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"From [Ruby's pattern matching guide](https://ruby-doc.org/3.2.2/syntax/pattern_matching_rdoc.html)\n", | |
"\n", | |
"> Additionally, when matching custom classes, the expected class can be specified as part of the pattern and is checked with ===\n", | |
"\n", | |
"This means that we cold create _matchers_ for our pattern matches.\n", | |
"\n", | |
"Let's try to create two classes implementing, on their singletons', a `===` method" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 100, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"module Matching\n", | |
" class Empty\n", | |
" def self.===(other)\n", | |
" return false unless other.respond_to?(:empty?)\n", | |
"\n", | |
" other.empty?\n", | |
" end\n", | |
" end\n", | |
"\n", | |
" class Filled\n", | |
" def self.===(other)\n", | |
" return false unless other.respond_to?(:empty?)\n", | |
"\n", | |
" !other.empty?\n", | |
" end\n", | |
" end\n", | |
"end\n", | |
"\n", | |
"nil" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"The idea is to match an object responding to the `empty?` _interface_ and match whenever the object is or is not `empty?`.\n", | |
"\n", | |
"This is the method using pattern matching" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 101, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def match(object)\n", | |
" case object\n", | |
" in Symbol, Matching::Filled\n", | |
" 'It is filled'\n", | |
" in Symbol, Matching::Empty\n", | |
" 'It is empty'\n", | |
" in Symbol, Matching::Array::Of::Integer\n", | |
" 'Is an array of integers'\n", | |
" else\n", | |
" 'Not matched'\n", | |
" end\n", | |
"end\n", | |
"\n", | |
"nil" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Examples follows" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 102, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"It is filled\"" | |
] | |
}, | |
"execution_count": 102, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"object = [:foo, 'xxx']\n", | |
"match(object)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 103, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"It is empty\"" | |
] | |
}, | |
"execution_count": 103, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"object = [:foo, '']\n", | |
"match(object)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 104, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"Not matched\"" | |
] | |
}, | |
"execution_count": 104, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"object = [:foo]\n", | |
"match(object)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Let's also try with typed arrays" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 105, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"module Matching\n", | |
" class Array\n", | |
" def self.===(other)\n", | |
" other.instance_of?(::Array)\n", | |
" end\n", | |
"\n", | |
" module Of\n", | |
" class Integers < Array\n", | |
" def self.===(other)\n", | |
" return false unless super\n", | |
"\n", | |
" other.all? { _1.instance_of?(::Integer) }\n", | |
" end\n", | |
" end\n", | |
"\n", | |
" class Strings < Array\n", | |
" def self.===(other)\n", | |
" return false unless super\n", | |
"\n", | |
" other.all? { _1.instance_of?(::String) }\n", | |
" end\n", | |
" end\n", | |
" end\n", | |
" end\n", | |
"end\n", | |
"\n", | |
"def match_array(object)\n", | |
" case object\n", | |
" in Symbol, Matching::Array::Of::Integers\n", | |
" 'Is an array of integers'\n", | |
" in Symbol, Matching::Array::Of::Strings\n", | |
" 'Is an array of strings'\n", | |
" else\n", | |
" 'Not matched'\n", | |
" end\n", | |
"end\n", | |
"\n", | |
"nil" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 106, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"Not matched\"" | |
] | |
}, | |
"execution_count": 106, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"object = [:foo, ['a', 1]]\n", | |
"match_array(object)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 107, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"Is an array of integers\"" | |
] | |
}, | |
"execution_count": 107, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"object = [:foo, [1, 2]]\n", | |
"match_array(object)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 108, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"Is an array of strings\"" | |
] | |
}, | |
"execution_count": 108, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"object = [:foo, ['1', '2']]\n", | |
"match_array(object)" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Ruby 3.2.2", | |
"language": "ruby", | |
"name": "ruby" | |
}, | |
"language_info": { | |
"file_extension": ".rb", | |
"mimetype": "application/x-ruby", | |
"name": "ruby", | |
"version": "3.2.2" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment