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.
The structure of the article is a bunch of definitions that build off of prior definitions, and I think it's best to just view the definitions as applying within the article and overriding any external existing definitions. It's a little unfortunate and unsettling the two of the early definitions (getter and setter) already mean something else in JS, but the terminology choice is trivial in a certain sense and doesn't affect the concepts laid out in the article.
I did find it confusing at first, though. Maybe it could have used a disclaimer, or maybe the author should have chosen different terms (maybe "producer" instead of "getter" and "consumer" instead of "setter"?). I also find it unintuitive to call console.log a "setter", and "consumer" feels better to me.
This was something that tripped me up when I first started university math courses. In more elementary courses, concepts are few and simple enough that you have a fairly consistent set of terminology that applies everywhere. Then I cracked open Spivak's and other higher-level textbooks, and each one is defining its own mutually-incoherent terminology.
It took a bit of adjusting, but local terminology is a lot more readable than if everybody tried to use universally-unambiguous language.
It's the main mechanism in JS to define functional accessors, at least since ECMA-Script 3. Before this, the terms have been used loosely, as well, for explicit accessor methods (like in Java).
[Edit] Correction: should have been, at least since ECMA-262 5th Ed.
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.
Author here: the inspiration for this article came primarily from Erik Meijer and this talk https://www.youtube.com/watch?v=sTSQlYX5DU0 where he talks a lot about Getters and Setters in Scala, although he's not focused on Scala (Erik has worked on C#, Haskell, and is now working on a new PHP-like language at Facebook).
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). 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.
> 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.
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:
I know it's not too bad to write callback based async... been doing it for a couple decades (first java, then flash adapters or hidden frames, then xhr). I just find if looks/feels much cleaner with async functions and promises.
I have to admit, I didn't like promises and preferred node style callbacks until async/await and have been using it with babel since before the name change.
Or as an alternative one can use a generator, they are built for that purpose AND are supported by a wide range of other JS constructs without any need to create iterators manually and all that.
function * getNext(){
let i = 2;
while(true){
i = i * 2;
yield i;
}
}
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.
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.
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.
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'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."
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.
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.