Skip to content

Instantly share code, notes, and snippets.

@DrBoolean
Created December 23, 2015 19:55
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DrBoolean/dbdeec22580cf95b1527 to your computer and use it in GitHub Desktop.
Save DrBoolean/dbdeec22580cf95b1527 to your computer and use it in GitHub Desktop.
Comonads are objects js style.
// 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