Skip to content

Instantly share code, notes, and snippets.

@havenwood
Last active April 6, 2025 19:33
Show Gist options
  • Save havenwood/015713e2e00c54280af16e11c2d09091 to your computer and use it in GitHub Desktop.
Save havenwood/015713e2e00c54280af16e11c2d09091 to your computer and use it in GitHub Desktop.
An example of a Rust-like `Option` enum in Ruby, see https://doc.rust-lang.org/rust-by-example/std/option.html
require_relative 'option'
include Option
# An integer division that doesn't `raise ZeroDivisionError`
def checked_division(dividend, divisor)
if divisor.zero?
# Failure is represented as the `None` variant
None[]
else
# Result is wrapped in a `Some` variant
Some[dividend / divisor]
end
end
# This function handles a division that may not succeed
def try_division(dividend, divisor)
# `Option` values can be pattern matched, just like other enums
case checked_division(dividend, divisor)
in None
puts "#{dividend} / #{divisor} failed!"
in Some(quotient)
puts "#{dividend} / #{divisor} = #{quotient}"
end
end
try_division(4, 2)
#>> 4 / 2 = 2
try_division(1, 0)
#>> 1 / 0 failed!
require 'singleton'
module Option
def self.from(value)
return None[] if value.nil?
Some[value]
end
Some = Data.define(:value) do
include Comparable
def <=>(other)
return 1 if other.is_a?(None)
value <=> other.value
end
alias unwrap value
alias unwrap_or_else value
def unwrap_or(_default) = value
def expect(_message) = value
def and(other) = other.is_a?(Some) ? other : None[]
def and_then = yield value
def or(_other) = self
def or_else = self
def xor(other) = other.is_a?(Some) ? None[] : self
def each
yield value
self
end
def map = Some[yield value]
def filter = yield(value) ? self : None[]
def flat_map(&) = map(&).flatten
def flatten
case value
in None | Some
value
else
self
end
end
def map_or(_default) = yield value
alias map_or_else map_or
def some? = true
def none? = false
def some_and? = yield value
alias none_or? some_and?
end
None = Data.define
class None
class UnwrapError < StandardError
def initialize(message = 'cannot unwrap a `None` value') = super
end
include Comparable
include Singleton
def <=>(other) = other.is_a?(Some) ? -1 : 0
class << self
alias [] instance
end
def unwrap = raise UnwrapError
def unwrap_or_else = yield
def unwrap_or(default) = default
def expect(message) = raise UnwrapError, message
def and(_other) = self
def and_then = self
def or(other) = other.is_a?(Some) ? other : self
alias or_else unwrap_or_else
alias xor or
def each = self
alias map each
alias filter each
alias flat_map each
alias flatten each
alias map_or unwrap_or
def map_or_else(default) = default.call
def some? = false
def none? = true
alias some_and? some?
alias none_or? none?
end
module Refinement
refine Object do
def to_option = Option::Some[self]
end
refine NilClass do
def to_option = Option::None[]
end
end
end
module Kernel
private
def Option(value) = Option.from(value)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment