Skip to content

Instantly share code, notes, and snippets.

@Whitespace
Created April 9, 2013 17:04
Show Gist options
  • Select an option

  • Save Whitespace/5347441 to your computer and use it in GitHub Desktop.

Select an option

Save Whitespace/5347441 to your computer and use it in GitHub Desktop.

Revisions

  1. Whitespace created this gist Apr 9, 2013.
    136 changes: 136 additions & 0 deletions monad.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,136 @@
    require "minitest/autorun"

    module Monad
    def return(val)
    self.class.new(val)
    end

    def bind(&block)
    self.return yield @value
    end
    end

    class Identity
    include Monad
    attr_reader :value

    def initialize(value)
    @value = value
    self
    end

    def ==(value)
    @value == value
    end
    end

    describe Identity do
    it "#bind is a left-identity with respect to return" do
    value = rand(100000)
    monad = Identity.new(value)

    monad.bind do |v|
    Identity.new(v)
    end.must_equal(monad)
    end

    it "#return is a right-identity with respect to bind" do
    value = rand(100000)
    rand1 = rand(100000)
    f = lambda { |v| v + rand1 }
    monad = Identity.new(value)

    monad.bind(&f).must_equal f.call(value)
    end

    it "#bind is associative" do
    value = rand(100000)
    rand1 = rand(100000)
    rand2 = rand(100000)
    f = lambda { |v| v + rand1 }
    g = lambda { |v| v + rand2 }
    monad = Identity.new(value)

    monad.bind do
    f.call(g.call(value))
    end.must_equal monad.bind(&f).bind(&g)
    end
    end

    module Just
    def just
    self
    end
    end

    class Nothing
    def self.just
    self
    end

    def just
    self.class
    end
    end

    class Object
    include Just
    end

    class Maybe
    include Monad

    attr_reader :value

    def initialize(value)
    @value = value
    end

    def fetch
    @value.just
    end

    def bind(&block)
    self.return case fetch
    when Nothing then Nothing
    else yield @value
    end
    end

    def ==(value)
    fetch == value
    end
    end

    describe Maybe do
    it "#bind is a left-identity with respect to return" do
    value = rand(100000)
    monad = Maybe.new(value)

    monad.bind do |v|
    Maybe.new(v)
    end.must_equal(monad)
    end

    it "#return is a right-identity with respect to bind" do
    value = rand(100000)
    rand1 = rand(100000)
    f = lambda { |v| v + rand1 }
    monad = Maybe.new(value)

    monad.bind(&f).must_equal f.call(value)
    end

    it "#bind is associative" do
    value = rand(100000)
    rand1 = rand(100000)
    rand2 = rand(100000)
    f = lambda { |v| v + rand1 }
    g = lambda { |v| v + rand2 }
    monad = Maybe.new(value)

    monad.bind do
    f.call(g.call(value))
    end.must_equal monad.bind(&f).bind(&g)
    end
    end