Skip to content

Instantly share code, notes, and snippets.

@FbN
Created March 9, 2021 16:12
Show Gist options
  • Save FbN/65fda3f881420b36e041a0b9f0964aa6 to your computer and use it in GitHub Desktop.
Save FbN/65fda3f881420b36e041a0b9f0964aa6 to your computer and use it in GitHub Desktop.
Clojure Protocols Inspired last param type polimorfism
export const of = x => ({ '@@type': 'A', x })
export const f1 = a => a.x
f1.arity = 1
export const f2 = c => a => a.x + c
f2.arity = 2
export const f3 = d => c => a => a.x + c + d
f3.arity = 3
export const of = y => ({ '@@type': 'B', y })
export const f1 = b => b.y
f1.arity = 1
export const f2 = c => b => c + b.y
f2.arity = 2
export const f3 = d => c => b => d + c + b.y
f3.arity = 3
import * as A from './A.js'
import * as B from './B.js'
import protocol from './protocol.js'
const _ = protocol({ A, B })
export const of = a => b => ({ '@@type': 'C', a, b })
export const concat = v => c => ({ a: _.f2(v)(c.a), b: _.f2(v)(c.b) })
concat.arity = 2
import * as A from './A.js'
import * as B from './B.js'
import * as C from './C.js'
import protocol from './protocol.js'
const _ = protocol({ A, B, C })
const a = A.of('.A2.')
const b = B.of('.B2.')
const a2 = {
...a,
x: '.A2.'
}
console.assert(_.f1(a) === A.f1(a))
console.assert(_.f1(b) === B.f1(b))
console.assert(_.f1(b) !== A.f1(b))
console.assert(_.f2('x')(a) === A.f2('x')(a))
console.assert(_.f2('y')(b) === B.f2('y')(b))
console.assert(_.f2('y')(b) !== A.f2('y')(b))
console.assert(_.f2('x')(a2) === A.f2('x')(a2))
console.assert(_.f2('x')(a2) !== B.f2('x')(a2))
const err = m => {
throw new Error(m)
}
export default function protocol (mods) {
const fSet = Object.entries(mods).reduce(
(fSet, [tn, ns]) => ({
...fSet,
...Object.entries(ns)
.filter(e => typeof e[1] === 'function' && e[1].arity)
.reduce(
(rSet, [fn, f]) => ({
...rSet,
[fn]:
fSet[fn] && fSet[fn] !== f.arity
? err('arity')
: f.arity
}),
{}
)
}),
{}
)
const f = fn => {
!fSet[fn] && err('not implemented')
switch (fSet[fn]) {
case 1:
return v => mods[v['@@type']][fn](v)
case 2:
return v => v2 => mods[v2['@@type']][fn](v)(v2)
case 3:
return v => v2 => v3 => mods[v3['@@type']][fn](v)(v2)(v3)
case 4:
return v => v2 => v3 => v4 =>
mods[v4['@@type']][fn](v)(v2)(v3)(v4)
case 5:
return v => v2 => v3 => v4 => v5 =>
mods[v5['@@type']][fn](v)(v2)(v3)(v4)(v5)
case 6:
return v => v2 => v3 => v4 => v5 => v6 =>
mods[v6['@@type']][fn](v)(v2)(v3)(v4)(v5)(v6)
}
}
return Object.keys(fSet).reduce(
(out, fn) => ({
...out,
[fn]: f(fn)
}),
{}
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment