Skip to content

Instantly share code, notes, and snippets.

@archaeron
Last active December 21, 2015 01:39
Show Gist options
  • Save archaeron/6229708 to your computer and use it in GitHub Desktop.
Save archaeron/6229708 to your computer and use it in GitHub Desktop.
Functors and Monoids
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