Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save alessandro-fazzi/79b59186e2f81153f94a7952dbe4ceb2 to your computer and use it in GitHub Desktop.
Save alessandro-fazzi/79b59186e2f81153f94a7952dbe4ceb2 to your computer and use it in GitHub Desktop.
[Study] Custom matchers for pattern matching
Display the source blob
Display the rendered blob
Raw
{
"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