raganwald.github.com icon indicating copy to clipboard operation
raganwald.github.com copied to clipboard

Comments & Questions on "Why Y?"

Open norswap opened this issue 5 years ago • 0 comments

Hey Reginald!

I've been reading "Why Y? Deriving the Y Combinator in JavaScript" with great pleasure. I was somewhat familiar with the concepts, but following through really helped them click. Thank you!

My process of understanding did involve tinkering with some code, and it seemed to me that the code for the various combinators was at times less simple than it could have been.

For instance, your formulate the why bird as:

const why = fn =>
(
    maker =>
        (...args) => fn(maker(maker), ...args)
)(
    maker =>
        (...args) => fn(maker(maker), ...args)
);

Why not opt for the more intuitive:

const why = fn =>
{
    const wrapper = (...args) => fn(wrapper, ...args);
    return wrapper;
};

I do understand the interest of presenting the first one, it does help with understanding the classical approach based on lambda-calculus which enables recursion without names (the real mind-bender here), but I'd much rather have the second one in my actual code!

In fact, I found it easier to understand the anonymous version by working backward from the version that uses the wrapper binding.

You do say that of a combinator that "It cannot create a named function expression." and "It cannot declare any bindings other than via parameters." But since you relax some other requirements for the idiomatic version, surely those could be relaxed too in practice.

The difference is even more dramatic regarding the long-tailed widowbird/trampoline:

const longtailed =
  fn =>
    (...initialArgs) => {
      let value =
        (x => x(x))(
          maker =>
            (...args) =>
              fn((...argsmm) => new Thunk(() => maker(maker)(...argsmm)), ...args)
        )(...initialArgs);

      while (value instanceof Thunk) {
        value = value.evaluate();
      }

      return value;
    };

My simplified version:

const longtailed = fn => (...args0) =>
{
    const wrapper = (...args) =>
        new Thunk(() => fn(wrapper, ...args));

    let value = fn(wrapper, ...args0);

    while (value instanceof Thunk)
        value = value.evaluate();

    return value;
};

Finally, a question, just to be sure I understood correctly. You write:

Let’s begin our cleanup by moving Thunk inside our function. This has certain technical advantages if we ever create a recursive program that itself returns thunks.

Do you mean that we want to distinguish the thunks returned by a why-form function passed to to the trampoline from the thunk that the trampoline uses internally?

Thanks again, and cheers!

norswap avatar Apr 03 '19 14:04 norswap