Last active
December 21, 2015 01:39
-
-
Save archaeron/6229708 to your computer and use it in GitHub Desktop.
Functors and Monoids
This file contains 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
class Monoid | |
mempty: -> | |
throw new Error "mempty not implemented" | |
mappend: -> | |
throw new Error "mappend not implemented" | |
mconcat: -> | |
throw new Error "mconcat not implemented" | |
class Option extends Monoid | |
mempty: -> | |
new None | |
@of: (value) -> | |
new Some(value) | |
# without pattern matching is there another way besides instanceof? | |
mappend: (otherOption) -> | |
if @ instanceof None | |
otherOption | |
else if otherOption instanceof None | |
@ | |
else if @.value instanceof Monoid and otherOption.value instanceof Monoid | |
new Some(@.value.mappend otherOption.value) | |
class Some extends Option | |
constructor: (@value) -> | |
fmap: (func) -> | |
new Some(func @value) | |
# could this really be so simple? | |
bind: (func) -> | |
func @value | |
toString: -> | |
"Some(#{@value})" | |
class None extends Option | |
fmap: -> | |
new None | |
bind: -> | |
new None | |
toString: -> | |
'None' | |
testOption = (desc, value, shouldBe) -> | |
test = value.toString() is shouldBe | |
testResult = if test | |
"✓ #{desc}" | |
else | |
"✖ #{desc}: #{value.toString()} should be #{shouldBe}" | |
console.log testResult | |
none = new None | |
some1 = new Some(1) | |
testOption 'Option.of', Option.of(1), 'Some(1)' | |
testOption 'None', none, 'None' | |
testOption 'Some(1)', some1, 'Some(1)' | |
succ = (number) -> | |
number + 1 | |
testOption "fmap succ of none", none.fmap(succ), "None" | |
testOption "fmap succ of Some(1)", some1.fmap(succ), "Some(2)" | |
testOption "None.mempty", none.mempty(), "None" | |
testOption "Some(1).mempty", some1.mempty(), "None" | |
testOption "Some(1) mappend None", some1.mappend(none), "Some(1)" | |
someNone = new Some(new None) | |
someSome50 = new Some(new Some(50)) | |
testOption "Mappend Monoids", someNone.mappend(someSome50), "Some(Some(50))" | |
div = (x, y) -> | |
Math.floor x / y | |
half = (x) -> | |
if x % 2 == 0 | |
new Some (div x, 2) | |
else | |
new None | |
some3 = new Some(3) | |
some4 = new Some(4) | |
testOption 'bind half to some3', (some3.bind half), 'None' | |
testOption 'bind half to some4', (some4.bind half), 'Some(2)' | |
id = (x) -> | |
x | |
addX = (x) -> | |
(y) -> | |
x + y | |
add4 = addX(4) | |
add5 = addX(5) | |
compose1 = (func1, func2) -> | |
(x) -> | |
func1(func2(x)) | |
testOption 'Functor laws: fmap id == id', some1.fmap(id), 'Some(1)' | |
testOption 'Functor laws: fmap (f . g)', some1.fmap(compose1(add4, add5)), 'Some(10)' | |
testOption 'Functor laws: fmap f . fmap g', some1.fmap(add4).fmap(add5), 'Some(10)' | |
liftM2 = (T, f, ma, mb) -> | |
ma.bind (a) -> | |
mb.bind (b) -> | |
T.of(f(a, b)) | |
addPlus1 = (a, b) -> a + b + 1 | |
liftResult = liftM2(Option, addPlus1, new Some(1), new Some(2)) | |
testOption 'Lift', liftResult, 'Some(4)' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment