Skip to content

Instantly share code, notes, and snippets.

@guilleiguaran
Last active December 20, 2015 22:48
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guilleiguaran/6207355 to your computer and use it in GitHub Desktop.
Save guilleiguaran/6207355 to your computer and use it in GitHub Desktop.
Maybe monad
# Consider the option type Maybe(a), representing a value that is either a single value of type a, or no value at all.
# To distinguish these, we have two algebraic data type constructors: Just(x), containing the value x, or Nothing,
# containing no value.
#
# data Maybe(t) = Just(t) | Nothing
#
# We would like to be able to use this type as a simple sort of checked exception: at any point in a computation,
# the computation may fail, which causes the rest of the computation to be skipped and the final result to be Nothing.
# If all steps of the calculation succeed, the final result is Just(x) for some value x.
require 'singleton'
class Maybe
def self.wrap(value)
if value.nil? || (value.respond_to?(:empty?) && value.empty?)
Nothing.instance
else
Just.new(value)
end
end
def method_missing(method, *args, &block)
pass(&lambda{|value| Maybe.wrap(value.send(method, *args, &block))})
end
end
class Just < Maybe
attr_reader :value
def initialize(value)
@value = value
end
def pass(&block)
block.call(@value)
end
def empty?
false
end
def maybe(default_value)
@value
end
def inspect
"Just(#{@value.inspect})"
end
end
class Nothing < Maybe
include Singleton
def pass
self
end
def empty?
true
end
def maybe(default_value)
default_value
end
def inspect
"Nothing"
end
end
@guilleiguaran
Copy link
Author

Usage:

mx, my = Maybe.wrap(1), Maybe.wrap(1)
mx.pass{|v1| my.pass{|v2| v1 + v2}} # => 2

mx, my = Maybe.wrap(1), Maybe.wrap(nil)
mx.pass{|v1| my.pass{|v2| v1 + v2}} # => Nothing

mx, my = Maybe.wrap(nil), Maybe.wrap(1)
mx.pass{|v1| my.pass{|v2| v1 + v2}} # => Nothing

Maybe.wrap(nil).upcase.downcase # => Nothing
Maybe.wrap("Foo").upcase.downcase # => Just("foo")
Maybe.wrap("Foo").upcase.downcase.value # => "foo"

x = Maybe.wrap("Foo")
case x
  when Just then puts "The value is #{x.value}"
  when Nothing then puts "No value"
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment