const fl = require('fantasy-land') //- Textbook rose tree. //+ type RoseTree a = { value :: a, children :: [RoseTree a] } function RoseTree(value, children) { if (this.constructor !== RoseTree) return new RoseTree(value, children) Object.assign(this, { value, children }) } /* Setoid a => Setoid (RoseTree a) */ { RoseTree.prototype[fl.equals] = function ({ value, children }) { return this.value[fl.equals](value) && this.children.length === children.length && this.children.every((child, i) => child[fl.equals](children[i])) } } /* Ord a => Ord (RoseTree a) */ { RoseTree.prototype[fl.lte] = function ({ value, children }) { return this.value[fl.lte](value) ? this.value[fl.equals](value) ? this.children[fl.lte](children) : true : false } } /* Functor RoseTree */ { RoseTree.prototype[fl.map] = function (f) { return RoseTree(f(this.value), this.children.map(f)) } } /* Apply RoseTree */ { RoseTree.prototype[fl.ap] = function ({ value: f, children: fs }) { const { value: x, children: xs } = this return RoseTree(f(x), [].concat( xs.map(x => x.map(f)), fs.map(f => this.value.ap(f)) )) } } /* Applicative RoseTree */ { RoseTree[fl.of] = x => RoseTree(x, []) } /* Foldable RoseTree */ { RoseTree.prototype[fl.reduce] = function (f, acc) { return this.children.reduce( (acc, rt) => rt[fl.reduce](f, acc), f(acc, this.value)) } } /* Traversable RoseTree */ { RoseTree.prototype[fl.traverse] = function (T, f) { return this.children[fl.traverse](T, f)[fl.ap]( f(this.value).map(v => cs => RoseTree(v, cs)) ) } } /* Chain RoseTree */ { RoseTree.prototype[fl.chain] = function (f) { const { value: x, children: xs } = f(this.value) return RoseTree(x, [].concat(xs, this.children.map(x => x[fl.chain](f)) )) } } /* Extend RoseTree */ { RoseTree.prototype[fl.extend] = function (f) { return RoseTree(f(this), this.children.map(x => x[fl.extend](f))) } } /* Comonad RoseTree */ { RoseTree.prototype[fl.extract] = function () { return this.value } } module.exports = RoseTree