mostly-adequate-guide
mostly-adequate-guide copied to clipboard
example for composition law in ch10 for Applicative Functors is not clear/wrong
I might did not get it but I was trying to run the sample in the example for the composition law and the part below does not seem to work:
const u = IO.of(toUpperCase); const v = IO.of(concat('& beyond')); const w = IO.of('blood bath ');
const test = IO.of(compose).ap(u).ap(v).ap(w) test.unsafePerformIO() gives 'x.toUpperCase is not a function'
can anyone help me understand what am I doing wrong or if the sample is incorrect.
I was confused as well. Based on my testing, the implementation of compose
as defined in the appendix https://mostly-adequate.gitbook.io/mostly-adequate-guide/appendix_a#compose throws an error:
const compose = (...fns) => (...args) => fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0];
If we change its implementation to accept just two functions then it works:
const compose = curry((f, g) => (x) => f(g(x)))
Also, append
would make more sense here instead of concat
.
#+begin_src js :noweb no-export
<<js io applicative demo>>
<<js curry>>
<<js concat>>
// <<js compose>>
const compose = curry((f, g) => (x) => f(g(x)))
const toUpperCase = (x) => x.toUpperCase()
const u = IO.of(toUpperCase)
const v = IO.of(concat('& beyond'))
const w = IO.of('blood bath ')
const left = IO.of(compose).ap(u).ap(v).ap(w)
const right = u.ap(v.ap(w))
return {
left: left.unsafePerformIO(),
right: right.unsafePerformIO()
}
#+end_src
#+RESULTS:
: { left: '& BEYONDBLOOD BATH ', right: '& BEYONDBLOOD BATH ' }
I leave the full implementation below:
class IO {
constructor(fn) {
this.unsafePerformIO = fn;
}
// [util.inspect.custom]() {
// return 'IO(?)';
// }
// ----- Pointed IO
static of(x) {
return new IO(() => x);
}
// ----- Functor IO
map(fn) {
return new IO(compose(fn, this.unsafePerformIO));
}
// ----- Applicative IO
ap(f) {
return this.chain(fn => f.map(fn));
}
// ----- Monad IO
chain(fn) {
return this.map(fn).join();
}
join() {
return new IO(() => this.unsafePerformIO().unsafePerformIO());
}
}
const curry = (f) => {
const arity = f.length
return function currier(...args) {
if (args.length < arity) {
return currier.bind(null, ...args)
}
return f.apply(null, args)
}
}
const concat = curry((a, b) => a.concat(b))
// const compose = (...fs) => (...args) => {
// return fs.reduceRight(
// (result, f) => [f.apply(null, result)],
// args
// )[0]
// // alternatively:
// // return fs.reduceRight((result, f) => f.apply(null, [].concat(result)), args)
// }
const compose = curry((f, g) => (x) => f(g(x)))
const toUpperCase = (x) => x.toUpperCase()
const u = IO.of(toUpperCase)
const v = IO.of(concat('& beyond'))
const w = IO.of('blood bath ')
const left = IO.of(compose).ap(u).ap(v).ap(w)
const right = u.ap(v.ap(w))
return {
left: left.unsafePerformIO(),
right: right.unsafePerformIO()
}