
JavaScript Getter-Setter Pyramid - adgasf
https://staltz.com/javascript-getter-setter-pyramid.html
======
snek
This article seems to be making up its own terminology and patterns. In JS, a
getter is a function bound to a property that is called when that property is
accessed. (mdn.io/getter) and a setter is a function bound to a property that
is called when the property is set (mdn.io/setter). In JS there isn't any real
differentiation you need to make between a function with 0 arguments, a
function with 1 argument, and a function with N arguments. They're all
functions with some arity that you can call.

~~~
azangru
Yeah, this is the OOP perspective on getters and setters. Andre talks from the
FP perspective.

~~~
amelius
Setters have no equivalent in (pure) FP.

~~~
com2kid
Saying console.log is a setter seems horrifically wrong, console.log abstracts
out IO and doesn't necessarily mutate any state that is within the program. It
leaves the happy confines of the program's abstraction/memory model and puts
data out into The Real World.

That all said, within the model being established by the author, everything
does make sense, eventually, and the difference between IO output functions
and setters, for the sake of the abstractions that the author builds, is non-
existent.

------
acjohnson55
This is a pretty interesting ontology of one path generalization.

I find the getter/setter nomenclature a little imprecise. It makes me think of
interacting with fixed state, rather than input and output into completely
abstract "systems" on the other side of the function calls. To me,
provider/consumer seems a bit more on-the-mark.

It seems to me that there are other paths of generalization you could follow,
as well (e.g.
[https://leanpub.com/combinators](https://leanpub.com/combinators)). Even
though we all know that you can model...well... _anything_ with functions,
it's still enlightening and mindbending to explore the patterns for actually
doing so.

------
megous
> Writing the above code example with none of the abstractions in the Getter-
> Setter Pyramid requires more amount of code, which is also harder to read.
    
    
        let usersRange = (start, end, result, doneCb, errorCb) => {
          let xhr = new XMLHttpRequest();
          xhr.responseType = 'json';
          xhr.open('GET', 'http://jsonplaceholder.typicode.com/users/' + start)
          xhr.send();
          xhr.onerror = ev => errorCb();
          xhr.onload = ev => {
            result.push(xhr.response);
            if (start < end)
              return usersRange(start + 1, end, result, doneCb, errorCb);
            done(result);
          }
        };
    
        usersRange(0, 10, [], /* completion callbacks */);
    

Simpler, not really harder to read (half of it is just boilerplate around XHR
setup, which is abstractable away). The meat of it is onload handler.

~~~
tracker1
Not sure, but I think an async function with fetch is easier to read.

    
    
        const usersRange = async (start, end) => {
          var result = [];
          for (let current = start; current < end; current++) {
            result.push(await fetch(`http://jsonplaceholder.typicode.com/users/${current}`).then(r => r.json())
          }
          return result;
        }

~~~
megous

        let usersRange = (start, end, result, done) => {
          if (start == end)
            return done(null, result);
    
          GET('http://jsonplaceholder.typicode.com/users/' + start, 
            res => usersRange(start + 1, end, (result.push(res), result), done),
            err => done(err)
          )
        };
    

GET() just wraps the XHR code in my original post and provides access to
onload/onerror callbacks. I used XHR, to avoid using fetch/Promise, which is
part of the pyramid.

I mean, I prefer async/await. The point is that it's just not inherently that
much more verbose to write callback based async code. Composition would be
more complicated. You'd have to use some abstract helper methods, or nest the
callbacks. For example:

    
    
        chain(
          (_, next) => usersRange(0, 10, [], next),
    
          ([err, res], next) => {
             if (err)
                // blabla
        
             usersDoSomething(res),
          }
        )
    

vs:

    
    
        try {
           let res = await usersRange(0, 10),
           usersDoSomething(res),
        } catch(ex) {
           if (err)
              // bla bla
        }
    

You can do some pretty fun stuff with functions, extending JS syntax so it has
simulated goto for example:

    
    
        let users = [], nextUser = 0;
        labelledChain(
          (_, next) => GET('/users', next),
    
          'got-users', ([err, res], next) => {
             users = res;
             next();
          },
    
          'get-next-user', ([err, res], next) => {
             if (users[nextUser])
                GET('/users/' + users[nextUser].id, next);
             else
                doneCallback(null, users);
          },
    
          'got-next-user', ([err, res], next) => {
             users[nextUser] = res;
             nextUser++;
             next.goto('get-next-user');
          },
        )
    

Fun challenge implementing labelledChain so that this all works. :D

~~~
rounce
> extending JS syntax so it has simulated goto for example

JavaScript has goto, it's the `continue` statement[1].

1: [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Statements/continue#Using_continue_with_a_label)

~~~
megous
That's not a real goto. You can only use a label for breaking out of outer
loops. Useful for that, but not for other kinds of control flow control.

------
yuchi
Similar in concept to the General Theory of Reactivity, GTOR:
[https://github.com/kriskowal/gtor/](https://github.com/kriskowal/gtor/)

------
SeanLuke
I'm interested in this simple closure code from the article:

    
    
         function getGetNext() {
           let i = 2;
           return function getNext() {
             const next = i;
             i = i * 2;
             return next;
            }
         }
    

As a lisp coder [and not a JS coder], I'm wondering: why does the function
getNext() have to be named? Why not just say something like:

    
    
         function getGetNext() {
           let i = 2;
           return function() {
             const next = i;
             i = i * 2;
             return next;
            }
         }

~~~
kangoo1707
It doesn't have to be named, your second example still works

~~~
SeanLuke
I got the syntax right? Was completely guessing.

~~~
basil-rash
Yep. Nowadays you'd be more likely to see it as:

    
    
        return () => {
          const next = i;
          i = i * 2;
          return next;
        }
    

But in this case the two are equivalent.

------
inglor
There is a great talk by Erik Meijer (who invented Rx) showing this very
neatly: [https://channel9.msdn.com/Events/Lang-NEXT/Lang-
NEXT-2014/Ke...](https://channel9.msdn.com/Events/Lang-NEXT/Lang-
NEXT-2014/Keynote-Duality)

~~~
michaelsbradley
What would AsyncObservable look like in JS?

~~~
staltz
It would be like an Observable, where the Observer's next/error/complete are
changed from X=>() to X=>Promise where the Promise indicates the Observer is
done with the consumption, telling the producer that it can continue producing
more values.

~~~
michaelsbradley
If the producer is ready to produce one/more values prior to the Observer's
being ready to consume them, do those values go into a queue?

If there is more than one Observer of the same AsyncObservable, is there a
separate queue for each of them?

I'm thinking of AsyncSink[1] used by AsyncIterableX[2] (both of IxJS[3]) and
how they might be conceptually related to implementations of AsyncObservable.

[1]
[https://github.com/ReactiveX/IxJS/blob/master/src/asyncsink....](https://github.com/ReactiveX/IxJS/blob/master/src/asyncsink.ts)

[2]
[https://github.com/ReactiveX/IxJS/blob/master/src/asyncitera...](https://github.com/ReactiveX/IxJS/blob/master/src/asynciterable/asynciterablex.ts)

[3] [https://github.com/ReactiveX/IxJS](https://github.com/ReactiveX/IxJS)

------
vq
I thought this was going to be about lenses implemented in JavaScript, if it
was I didn't get it. The Haskell lens[1] package (which implements a type of
lenses referred to as van Laarhoven lenses after their discoverer Twan van
Laarhoven) builds up a hierarchy (pyramid if you will) of lenses with varying
degrees of generality and power.

[1]:
[http://hackage.haskell.org/package/lens-4.17#readme](http://hackage.haskell.org/package/lens-4.17#readme)

------
nenadg
There is no copyright on terms 'getter', 'setter' etc.

For the first part I didn't like the article since it's somewhere between fp
and oop, vague or foggy. But it's just my interpretation based/biased with
previous knowledge.

Once I assumed the article is conceptual and in harmony with it's definitions,
everything got much more sense.

I like it.

------
kowdermeister
My OCD kicked in at the end of the article looking at that pyramid. He should
have put a triangle on top :)

~~~
rbonvall
Maybe it should've been called "the getter-setter ziggurat."

------
tylerjwilk00
I'm sure the author knows more about the topic than me. However, it reads like
someone trying to rationalize something that isn't quite rational.

IE: Walking into an old antique store and attempting to explain the genius
logic behind it's organization. Shortly after the owner comes out and says,
"No, I just throw shit wherever."

------
whyonearth
> The cornerstone of JavaScript is the function.

JavaScript is specified in terms of objects, has richer facilities for dealing
with objects than functions, functions are objects but not vice-versa. So,
objects are the cornerstone. Callers are free to ignore a function's arity
even.

~~~
ben509
Yup, everything is nicer in Javascript if you're working with objects.

There's no type checking or syntactic sugar for dealing with first-class
functions, so complex functional programming leads to brutal stack traces and
if you ask a debugger what any part of this "pyramid" is, it tells you, "well,
it's a function."

If I'm using a parser combinator in Haskell, for instance, I can just say:

    
    
       liftA2 (+) parseInteger parseInteger
    

It's pretty clear (if you're familiar with functors) that I'm parsing two
integers and adding the result. And it has to typecheck, so 90% of my stupid
mistakes are caught by the compiler.

But, also, if I go into ghci, I can see what it is:

    
    
         :t liftA2
         Applicative f => (a -> b -> c) -> f a -> f b -> f c
    

So I can see the first argument (a -> b -> c) is a function with two
arguments, and the second, f a, and third, f b, are boxed in the Applicative,
and the result, f c, is boxed in the Applicative. That's how you can
understanding that it's combining two parsers using + and then the result is
itself a parser.

Javascript, even though it supposedly has all its type information at runtime,
really doesn't _know_ any of this because a function is a function and that's
all it knows. You could replicate liftA2 in Javascript, but it would be
entirely mysterious what's going on.

------
azangru
Is this the write-up of his talks?

I remember this one, from the Uphill Conference:
[https://www.youtube.com/watch?v=fdol03pcvMA](https://www.youtube.com/watch?v=fdol03pcvMA)

~~~
Vinnl
Yes, though depending on how long ago you were present at such a talk, the
talk might have covered just part of this.

------
vernie
JavaScript was a mistake.

------
gambler
JavaScript is truly the new Java. It even has design pattern articles now.

