Last active
December 19, 2015 06:09
-
-
Save Shinpeim/5909225 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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