8 ms·
JavaScript Getter-Setter Pyramid
- snek 8y agoThis 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.
- inglor 8y agoHey Gus, this actually does make some mathematical sense. See https://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Keynote-Duality https://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Ke...
- snek 8y agoI'm not saying it doesn't make sense, just that it ignores existing terminology of the language, which just leads to confusion down the road.
- azangru 8y agoYeah, this is the OOP perspective on getters and setters. Andre talks from the FP perspective.
- masswerk 8y agoHowever, in JS they have a specific [read, established] meaning, in the OOP sense. [It's a term established in the language specification.]
- ponyfleisch 8y ago> [It's a term established in the language specification.] I'd say JS getters/setters are syntactic sugar for the OOP concept the author is talking about.
- masswerk 8y agoIt'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.
- amelius 8y agoSetters have no equivalent in (pure) FP.
- com2kid 8y agoSaying 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.
- AzzieElbab 8y agoState monad?
- alangpierce 8y agoThe 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.
- JeremyBanks 8y agoThis 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.
- aaaaaaaaaab 8y agoYeah. source/sink would have been a more appropriate terminology IMO.
- karmakaze 8y agoThere are also existing terms which are much more fitting, e.g. provider and consumer.
- staltz 8y agoAuthor here: the inspiration for this article came primarily from Erik Meijer and this talk https://www.youtube.com/watch?v=sTSQlYX5DU0 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).
- inglor 8y agoThere is a great talk by Erik Meijer (who invented Rx) showing this very neatly: https://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Keynote-Duality https://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Ke...
- michaelsbradley 8y agoWhat would AsyncObservable look like in JS?
- staltz 8y agoIt 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 8y agoIf 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.ts https://github.com/ReactiveX/IxJS/blob/master/src/asyncsink.... [2] https://github.com/ReactiveX/IxJS/blob/master/src/asynciterable/asynciterablex.ts https://github.com/ReactiveX/IxJS/blob/master/src/asyncitera... [3] https://github.com/ReactiveX/IxJS https://github.com/ReactiveX/IxJS
- yuchi 8y agoSimilar in concept to the General Theory of Reactivity, GTOR: https://github.com/kriskowal/gtor/ https://github.com/kriskowal/gtor/
- kowdermeister 8y agoMy OCD kicked in at the end of the article looking at that pyramid. He should have put a triangle on top :)
- acjohnson55 8y agoThis 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 8y ago> 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.
- exceptione 8y agoShouldn't return nextUser(... be return usersRange(... ?
- megous 8y agoYes. Sorry, it was originaly all named nextUser, but I did miss that one rename.
- tracker1 8y agoNot 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 8y agolet 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
- azangru 8y agoIs 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 8y agoYes, though depending on how long ago you were present at such a talk, the talk might have covered just part of this.
- gcb0 8y agolol. nice and detailed april fools article. just wish it wasn't posted in December. > avoid global state by having a global name that hold the state
- ponyfleisch 8y ago> lol. nice and detailed april fools article. just wish it wasn't posted in December. > > avoid global state by having a global name that hold the state That state is not global though, you can use multiple getters from a gettergetter in parallel.
- gambler 8y agoJavaScript is truly the new Java. It even has design pattern articles now.
- SeanLuke 8y agoI'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 8y agoIt doesn't have to be named, your second example still works
- wbrasky 8y agonaming anonymous functions can provide some clarity in stack traces however
- SeanLuke 8y agoI got the syntax right? Was completely guessing.
- basil-rash 8y agoYep. 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.
- interesthrow2 8y agoOr 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; } } Here, no closure needed. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guid...
- whyonearth 8y ago> 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 8y agoYup, 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.
- vernie 8y agoJavaScript was a mistake.
- vq 8y agoI 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 8y agoThere 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.
- tylerjwilk00 8y agoI'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."