-
-
Save avernet/952737 to your computer and use it in GitHub Desktop.
State Monad in CoffeeScript
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
#### Monad - common code | |
# bind takes a step and processor, | |
# and combines them into a step | |
# (S t) -> (t -> S u) -> (S u) | |
bind = (step, processor) -> (container) -> | |
result = step container | |
(processor result.value) result.container | |
# result builds a step that returns just a result | |
# t -> S t | |
result = (value) -> (container) -> | |
{value: value, container: container} | |
#### General list monad | |
# L t: List -> {t, List} | |
# Create a processor that discards the current list, and creates a new one based on the parameters | |
create = (start, end) -> (list) -> | |
{value: null, container: [start..end]} | |
# Length is a step | |
length = (list) -> | |
{value: list.length, container: list} | |
# Add is processor taking the increment as parameter | |
add = (i) -> (list) -> | |
{value: null, container: e + i for e in list } | |
# Sum is a step returning the list unchanged with the sum of all the items | |
sum = (list) -> | |
{value: (v = 0; v += e for e in list; v), container: list } | |
chain = bind(create(1, 4), ()-> | |
bind(length, (l) -> | |
bind(add(l), () -> | |
sum | |
) | |
) | |
) | |
console.log(chain([]).value) |
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
# Let's construe the list monad not as a container, but as a function (step) | |
# General monadic type: M t: I -> O t | |
# Particular case of pipeline step: S t: X -> (X, t) | |
# Particular case of list: L t: -> List t | |
# Unit processor: t -> L t | |
unit = (x) -> -> [x] | |
# bind takes a step and processor, and combines them into a step | |
# (M t) -> (t -> M u) -> (M u) | |
bind = (m) -> (p) -> | |
-> r = []; (r.push u for u in (p t)()) for t in m(); r | |
# Double processor | |
double = (x) -> -> [2*x] | |
# Children age step: L t | |
childrenAge = -> [6, 8] | |
# Is teenager processor: t -> M t | |
isTeenager = (t) -> -> if 13 <= t <= 19 then [t] else [] | |
console.log ((bind childrenAge) double)() # [12, 16] | |
console.log ((bind ((bind childrenAge) double)) isTeenager)() # [16] |
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
# A monad is like a step | |
# that is a function: XML -> {result, XML} | |
# We can write the type of a step as S t | |
# e.g. S boolean for the validation step | |
# A processor is a function parameter -> step | |
# Processor: t -> S u | |
# e.g. Schema processor | |
# take a schema as parameter | |
# returns a step: XML -> {boolean, XML} | |
# schema -> S boolean | |
# e.g. Save-if is a processor | |
# take a boolean as parameter | |
# return a step: XML -> {(), XML} | |
# boolean -> S () | |
# Processor could be called step builder | |
# bind takes a step and processor, | |
# and combines them into a step | |
# (S t) -> (t -> S u) -> (S u) | |
bind = (stackOperation, continuation) -> (stack) -> | |
result = stackOperation stack | |
(continuation result.value) result.stack | |
# result builds a step that returns just a result | |
# t -> S t | |
result = (value) -> (stack) -> | |
{value: value, stack: stack} | |
# Example: posted XML, extract1, extract2, concat | |
# bind(extract(xpath), (e1) -> | |
# bind(extract(xpath), (e2) -> | |
# result(e1+e2) | |
# ) | |
# ) | |
push = (element) -> (stack) -> | |
newStack = [element].concat stack | |
{value: element, stack: newStack} | |
pop = (stack) -> | |
element = stack[0] | |
newStack = stack.slice 1 | |
{value: element, stack: newStack} | |
initialStack = [] | |
computation1 = bind \ | |
(push 4), -> bind \ | |
(push 5), -> bind \ | |
pop, (a) -> bind \ | |
pop, (b) -> | |
result a + ":" + b | |
computation2 = bind \ | |
(push 2), -> bind \ | |
(push 3), -> bind \ | |
pop, (a) -> bind \ | |
pop, (b) -> | |
result a + ":" + b | |
composed = bind \ | |
computation1, (a) -> bind \ | |
computation2, (b) -> | |
result a + ":" + b | |
finalResult = composed initialStack | |
console.log finalResult.value | |
# Let's implement a general list monad | |
# L t: List -> {t, List} | |
# Create a processor that discards the current list, and creates a new one based on the parameters | |
create = (start, end) -> (list) -> | |
{value: null, stack: [start..end]} | |
# Length is a step | |
length = (list) -> | |
{value: list.length, stack: list} | |
# Add is processor taking the increment as parameter | |
add = (i) -> (list) -> | |
{value: null, stack: e + i for e in list } | |
# Sum is a step returning the list unchanged with the sum of all the items | |
sum = (list) -> | |
{value: (v = 0; v += e for e in list; v), stack: list } | |
chain = bind(create(1, 4), ()-> | |
bind(length, (l) -> | |
bind(add(l), () -> | |
sum | |
) | |
) | |
) | |
console.log(chain([]).value) | |
# In the case where the step can't return a value, we can simplify | |
# L t to be List instead of List -> {t, List} | |
# Then a step builder is something that builds a list from a parameter: a list builder | |
# Combining list builder is done with foldMap | |
# But: less general then above, doesn't allow to implement a length |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment