Skip to content

Instantly share code, notes, and snippets.

@tylerjohnst
Last active February 8, 2019 21:32
Show Gist options
  • Save tylerjohnst/43b6592ab26c2fdb4a76f1ddbfc19a39 to your computer and use it in GitHub Desktop.
Save tylerjohnst/43b6592ab26c2fdb4a76f1ddbfc19a39 to your computer and use it in GitHub Desktop.
Don't do this in production, or do. I'm not your parent.
class Maybe
attr_reader :value
# @returns [Maybe]
def self.empty
@empty ||= new(nil)
end
def initialize(value, &block)
@value = value.is_a?(Maybe) ? value.value : value
@value = MaybeBlock.maybe(self, &block) if block_given?
end
# Returns the inner value or a fallback.
def value_or(fallback)
value || fallback
end
def ==(other_value)
if other_value.is_a?(Maybe)
value == other_value.value
else
value == other_value
end
end
# @returns [Maybe]
def call(method_name, *args)
if value && value.respond_to?(method_name)
Maybe.new value.public_send(method_name, *args)
else
Maybe.empty
end
end
end
class MaybeBlock < BasicObject
def self.maybe(value, &block)
new(value).instance_exec(&block).instance_variable_get('@value')
end
def initialize(value)
@value = value
end
def method_missing(*args)
@value = @value.call(*args)
end
end
if $0 == __FILE__
require 'minitest/autorun'
require 'minitest/spec'
Class.new(Minitest::Spec) do
it 'gracefully handles a nil object' do
assert_nil Maybe.new(nil).(:to_s).(:reverse).(:upcase).value
end
it 'returns nil if the resulting expression would throw an exception' do
assert_nil Maybe.new(:hello).(:rand).value
end
it 'is accepts other maybes' do
assert_equal 'Hello', Maybe.new(Maybe.new('Hello')).value
end
it 'returns a value or a another value if the monad is empty' do
assert_equal 'Yes', Maybe.empty.value_or('Yes')
end
it 'chains along the calls if they are correct' do
assert_equal "OLLEH", Maybe.new(:hello).(:to_s).(:split, '').(:reverse).(:join, '').(:upcase).value
end
it 'allows for checking equality' do
assert Maybe.empty == nil
assert Maybe.new('A') == Maybe.new('A')
end
it 'allows for hilarious block syntaxes' do
result = Maybe.new(:hello) do
to_s
split ''
reverse
join ''
upcase
end
assert_equal 'OLLEH', result.value
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment