Created
April 3, 2012 02:48
-
-
Save kputnam/2288938 to your computer and use it in GitHub Desktop.
IO monad in CoffeeScript
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
# Syntax sugar, more or less: wrap each monadic action with | |
# another function that binds to the next action `k`. | |
#################################################################### | |
confirm_ = (msg, k) -> | |
bind confirm(msg), k | |
alert_ = (msg, k) -> | |
bind alert(msg), k | |
prompt_ = (msg, detent = "", k) -> | |
bind prompt(msg, detent), k | |
setTTY_ = (tty, k) -> | |
bind setTTY(tty), k | |
# Examples | |
#################################################################### | |
test = | |
prompt_ "What's yer name?", "Ed", (name) -> | |
alert_ "Hi, #{name}!", -> | |
prompt_ "Wait, who are you?", "Jo", (conf) -> | |
# This is a poor example of still needing the non-wrapped | |
# actions. We want to avoid duplication, but both branches | |
# need to bind to the same action (unit). This is a poor | |
# example because we could just define next = unit "..." | |
# and then change both branches to alert_ "...", next. | |
next = if name == conf \ | |
then alert "Of course. Bai now!" | |
else alert "LIAR!" | |
bind next, -> | |
unit "Names: #{name}, #{conf}" | |
# This returns a log of all IO actions | |
exec(test, (x) -> console.log(x)) |
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
nullTTY = | |
confirm: (msg, k) -> k Math.random() > 0.5 | |
prompt: (msg, detent, k) -> k detent | |
alert: (msg, k) -> k null | |
# Browser and REPL "TTYs" | |
#################################################################### | |
if window? | |
defaultTTY = | |
confirm: (msg, k) -> k window.confirm(msg), | |
prompt: (msg, detent, k) -> k window.prompt(msg, detent), | |
alert: (msg, k) -> k window.alert(msg) | |
else | |
readline = require("readline") | |
reader = readline.createInterface(process.stdin, process.stdout, null) | |
defaultTTY = | |
confirm: (msg, k) -> reader.question(msg + " ", k) | |
prompt: (msg, detent, k) -> reader.question(msg + " ", k) | |
alert: (msg, k) -> k reader.write(msg + "\n") | |
# Actions | |
#################################################################### | |
confirm = (msg) -> (log, tty, k) -> | |
tty.confirm msg, (answer) -> | |
k tty: tty, value: answer, log: log.concat("confirm: #{msg} => #{answer}") | |
alert = (msg) -> (log, tty, k) -> | |
tty.alert msg, (answer) -> | |
k tty: tty, value: answer, log: log.concat("alert: #{msg}") | |
prompt = (msg, detent = "") -> (log, tty, k) -> | |
tty.prompt msg, detent, (answer) -> | |
k tty: tty, value: answer, log: log.concat("prompt: #{msg} => #{answer}") | |
setTTY = (tty) -> (log, tty, k) -> | |
k tty: tty, value: null, log: log.concat("setTTY") | |
# Combinators | |
#################################################################### | |
unit = (value) -> (log, tty, k) -> | |
k tty: tty, log: log, value: value | |
bind = (x, f) -> (log, tty, k) -> | |
x log, tty, (tuple) -> | |
f(tuple.value)(tuple.log, tuple.tty, k) | |
# Evaluators | |
#################################################################### | |
run = (computation, k) -> | |
computation [], defaultTTY, k | |
exec = (computation, k) -> | |
computation [], defaultTTY, (tuple) -> k tuple.log | |
eval = (computation, k) -> | |
computation [], defaultTTY, (tuple) -> k tuple.value | |
# Examples | |
#################################################################### | |
test = | |
bind prompt("What's yer name?", "Ed"), (name) -> | |
bind alert("Hi, #{name}!"), -> | |
bind prompt("Wait, who are you?", "Jo"), (conf) -> | |
next = if name == conf \ | |
then alert "Of course. Bai now!" | |
else alert "LIAR!" | |
bind next, -> | |
unit "Names: #{name}, #{conf}" | |
# This returns a log of all IO actions | |
exec(test, (x) -> console.log(x)) | |
# This returns the computed "Names: ..." value | |
# eval(test, (x) -> console.log(x)) | |
if exports? | |
exports.alert = alert | |
exports.prompt = prompt | |
exports.confirm = confirm | |
exports.setTTY = setTTY | |
exports.bind = bind | |
exports.unit = unit | |
exports.exec = exec | |
exports.nullTTY = nullTTY | |
exports.defaultTTY = defaultTTY | |
exports.test = test |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment