Skip to content

Instantly share code, notes, and snippets.

@Shinpeim
Last active December 19, 2015 06:09
Show Gist options
  • Save Shinpeim/5909225 to your computer and use it in GitHub Desktop.
Save Shinpeim/5909225 to your computer and use it in GitHub Desktop.
require "singleton"
class Option
def map(f)
flatmap(->(x){option(f.call(x))})
end
end
def option(value)
if value == nil
none
else
some(value)
end
end
class Some < Option
def initialize(value)
raise "can't wrap nil in 'Some'" if value == nil
@value = value
end
def get_or_else(default)
@value
end
def flatmap(f)
value = f.call(@value)
raise "Option#flatmap(f: T -> Option[T])" unless value.is_a?(Option)
value
end
def ==(option)
return false unless option.is_a? Some
@value == option.instance_eval{ @value }
end
end
def some(value)
Some.new(value)
end
class None < Option
include Singleton
def get_or_else(default)
default
end
def flatmap(f)
none
end
end
def none
None.instance
end
describe Option do
context "when given nil" do
it "should return none" do
expect(option(nil)).to eq(none)
end
end
context "when given some value" do
it "should return some(value)" do
expect(option(1)).to eq(some(1))
end
end
end
describe Some do
context "when given nil" do
it "should raise error" do
expect { some(nil) }.to raise_error
end
end
context "when given some value" do
subject { some(1) }
describe "#==" do
it "should equals to some(same_value)" do
expect(subject).to eq(some(1))
end
end
describe "#get_or_else(default)" do
it "should return a inner value" do
expect(subject.get_or_else(0)).to eq(1)
end
end
describe "#map(->(v){v * 2})" do
it "should return Some(2)" do
expect(subject.map(->(v){v * 2})).to eq(some(2))
end
end
describe "#flatmap(->(v){some(v * 2)})" do
it "should return Some(2)" do
expect(subject.flatmap(->(v){some(v * 2)})).to eq (some(2))
end
end
end
end
describe None do
describe "#==" do
it "should equals to none" do
expect(none).to eq(none)
end
end
describe "#get_or_else(default)" do
it "should return a default" do
expect(none.get_or_else(0)).to eq(0)
end
end
describe "#map(fun)" do
it "should return none" do
expect(none.map(->(v){v * 2})).to eq(none)
end
end
describe "#flatmap(fun)" do
it "should return none" do
expect(none.flatmap(->(v){v * 2})).to eq(none)
end
end
end
describe "nested Some#flatmap & Some#flatmap" do
subject {
option(2).flatmap(->(a){ option(3).flatmap(->(b){ option(a * b) })})
}
it { should == some(6) }
end
describe "nested Some#map & Some#flatmap" do
subject {
some(2).flatmap(->(a){ some(3).map(->(b){a * b})})
}
it { should == some(6) }
end
describe "nested Some#map & None#flatmap" do
subject {
none.flatmap(->(a){ some(3).map(->(b){a * b})})
}
it { should == none }
end
describe "nested None#map & None#flatmap" do
subject {
some(2).flatmap(->(a){ none.map(->(b){a * b})})
}
it { should == none }
end
describe "モナド則を満たしていることをチェック" do
let(:r){ ->(x){ option(x) } }
it "(return x) >>= f == f x" do
f = ->(x){ option(x * 2) }
expect(r.call(2).flatmap(f)).to eq(f.call(2))
end
it "m >>= return == m" do
m = some(2)
expect(m.flatmap(r)).to eq(m)
end
it '(m >>= f) >>= g == m >>= (\x -> f x >>= g)' do
m = some(2)
f = ->(x){ option(x * 2) }
g = ->(x){ option(x * 3) }
expect(m.flatmap(f).flatmap(g)).to eq(m.flatmap(->(x){ f.call(x).flatmap(g) }))
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment