Skip to content

Instantly share code, notes, and snippets.

@CreaturePhil
Created August 31, 2017 04:15
Show Gist options
  • Save CreaturePhil/b7f85d0bba14e942c5c7f81396833ee7 to your computer and use it in GitHub Desktop.
Save CreaturePhil/b7f85d0bba14e942c5c7f81396833ee7 to your computer and use it in GitHub Desktop.
let daggy = {}
daggy.tagged = (typeName, fields) => {
function constructor() {
const values = Array.prototype.slice.call(arguments);
const proto = {
toString() {
return typeName + '(' + values.toString() + ')'
}
}
proto.__proto__ = constructor.prototype
const construct = Object.create(proto)
for (let i = 0; i < fields.length; i++) {
construct[fields[i]] = values[i]
}
return construct
}
constructor.toString = () => typeName
constructor.is = (val) => {
const vs = val.toString()
const index = vs.indexOf('(')
const name = index >= 0 ? vs.slice(0, index) : vs
return typeName === name
}
return constructor
}
const Point3D = daggy.tagged('Point3D', ['x', 'y', 'z'])
console.log(Point3D.toString()) // 'Point3D'
const a = Point3D(1, 2, 3)
console.log(a) // { x: 1, y: 2, z: 3 }
console.log(a.x == 1 && a.y == 2 && a.z == 3) // true
console.log(a.toString()) // 'Point3D(1,2,3)'
console.log(Point3D.is(a)) // true
Point3D.prototype.scale = function(n) {
return Point3D(this.x * n, this.y * n, this.z * n)
}
const b = a.scale(2)
console.log(b) // { x: 2, y: 4, z: 6 }
console.log(b.toString()) // 'Point3D(2, 4, 6)'
daggy.taggedSum = (typeName, constructors) => {
let typeRep = function() {}
Object.keys(constructors).forEach(name => {
typeRep[name] = daggy.tagged(typeName + '.' + name, constructors[name])
typeRep[name].prototype = typeRep.prototype
})
typeRep.is = (val) => {
const vs = val.toString()
return typeName === vs.slice(0, vs.indexOf('.'))
}
typeRep.prototype.cata = function(fs) {
const typeRepString = this.toString()
const dotIndex = typeRepString.indexOf('.')
const parenIndex = typeRepString.indexOf('(')
const constructor = dotIndex >= 0 ?
typeRepString.slice(dotIndex + 1, parenIndex) : typeRepString
const fields = constructors[constructor].reduce((acc, cur) => (
this[cur] ? acc.concat([this[cur]]) : acc
), [])
return fs[constructor].apply(fs, fields)
}
return typeRep
}
const Option = daggy.taggedSum('Option', {
Some: ['x'],
None: [],
})
const c = Option.Some(1)
console.log(c) // { x: 1 }
console.log(c.toString()) // 'Option.Some(1)'
console.log(Option.Some.is(c)) // true
console.log(Option.is(c)) // true
console.log(Option.None.is(Option.None)) // true
console.log(Option.is(Option.None)) // true
console.log(Option.None.toString()) // 'Option.None'
console.log(Option.Some.toString()) // 'Option.Some'
Option.prototype.map = function(f) {
return this.cata({
Some: (x) => Option.Some(f(x)),
None: () => this,
})
}
const d = c.map(x => x+1)
console.log(d) // { x: 2 }
console.log(d.toString()) // 'Option.Some(2)'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment