Created
December 23, 2015 19:55
-
-
Save DrBoolean/dbdeec22580cf95b1527 to your computer and use it in GitHub Desktop.
Comonads are objects js style.
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
// http://www.haskellforall.com/2013/02/you-could-have-invented-comonads.html | |
const daggy = require('daggy'); | |
const {toUpper, prop, identity, range, curry, compose} = require('ramda'); | |
const Config = daggy.tagged("opts") | |
Config.prototype.inspect = function() { | |
return `Config(${this.opts})` | |
} | |
const Builder = daggy.tagged("f") | |
Builder.prototype.map = function(f) { | |
return Builder(compose(f, this.f)); | |
} | |
Builder.prototype.duplicate = function() { | |
return Builder(curry((opts2, opts1) => this.f(opts1.concat(opts2)))) | |
} | |
Builder.prototype.extend = function(f) { | |
return Builder((opts2) => f((opts1) => this.f(opts1.concat(opts2)))) | |
} | |
Builder.prototype.extract = function() { | |
return this.f([]) | |
} | |
// defaultConfig :: [Option] -> Config | |
const defaultConfig = (opts) => Config(["-Wall"].concat(opts)) | |
// profile :: ([Option] -> Config) -> Config | |
const profile = (f) => f(["-prof", "-auto-all"]) | |
// goFaster :: ([Option] -> Config) -> Config | |
const goFaster = (f) => f(['-O2']) | |
profile(defaultConfig); | |
//=> Config(-Wall,-prof,-auto-all) | |
// We can't compose profile with goFaster because we're stuck with a final Config result so comonad to the rescue: | |
Builder(defaultConfig).extend(profile).extend(goFaster).extract(); | |
//=> Config('-Wall', '-prof', '-auto-all', '-O2') | |
const Iterator = daggy.tagged('head', 'tail') | |
Iterator.prototype.extract = function() { | |
return this.head | |
} | |
Iterator.prototype.extend = function(f) { | |
return this.tail ? Iterator(f(this), this.tail.extend(f)) : this; | |
} | |
const next = ({head: h, tail: t}) => t.head | |
// just building up a history | |
const exampleHistory = ['exit', 'bye', 'hello?', 'eat flaming death', '^C', '^D'].reduce((acc, s) => Iterator(s, acc), Iterator('', null)); | |
// since next "drops out" of the context, we extend it to keep chaining. | |
exampleHistory.extend(next).extend(next).extract(); | |
//=> eat flaming death | |
const Thermostat = daggy.tagged('t', 'f') | |
Thermostat.prototype.map = function(f) { | |
return Thermostat(this.t, compose(f, this.f)) | |
} | |
Thermostat.prototype.extract = function() { | |
return this.f(this.t) | |
} | |
Thermostat.prototype.duplicate = function() { | |
return Thermostat(this.t, (t) => Thermostat(t, this.f)) | |
} | |
Thermostat.prototype.extend = function(f) { | |
return this.duplicate().map(f) | |
} | |
const kelvinToCelsius = (t) => t - 273.15 | |
const initialThermostat = Thermostat(298.15, kelvinToCelsius) | |
const up = ({f: f, t: t}) => f(t + 1) | |
const down = ({f: f, t: t}) => f(t - 1) | |
const toString = ({f: f, t: t}) => f(t) + " celsius" | |
initialThermostat.extend(up).extend(toString).extract(); | |
//=> 26 celsius |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment