mostly-adequate-guide icon indicating copy to clipboard operation
mostly-adequate-guide copied to clipboard

example for composition law in ch10 for Applicative Functors is not clear/wrong

Open deleite opened this issue 4 years ago • 1 comments

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.

deleite avatar Nov 07 '20 08:11 deleite

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()
}

sevillaarvin avatar Jul 24 '22 12:07 sevillaarvin