14 ms·
"Intuitive" is too subject. What we're interested in is simplicity. And this is an objective measure. This (A): partial(Math.max, 2) is objectively simpl
by wellpast 9y ago
"Intuitive" is too subject.
What we're interested in is simplicity. And this is an objective measure.
This (A):
partial(Math.max, 2)
is objectively simpler than this (B):
function atLeastTwo(someNumber) {
return Math.max(2, someNumber);
}
Why is it objective? Because it expresses the same exact value (i.e., function) with an objectively fewer number of concepts.
When (A) is unfamiliar it feels less intuitive, but with practice/experience that sense of unintuitive goes away, and then you are left with (B) a simpler expression -- and therefore less cognitive load on the developer trying to make progress.
- pacala 9y agoUsing a good function syntax [Scala], the pointed version is: { x => Math.pow(2, x) } Obvious it's a function of 1 argument. The pointfree variant requires 1 extra concept [partial], plus building a mental map of the arguments, to reconstruct what kind of a function [or value!] the pointfree expression represent. Furthermore, implementing the reverse check is syntactically symmetric in the pointed version: { x => Math.pow(x, 2) } Whereas the pointfree version, assuming the function is not commutative, accumulates even more concepts: partial(flip(Math.pow), 2)
- mrkgnao 9y agoUsing even better (PureScript) syntax, the pointfree version of the latter can be written in notation similar to a hom-functor. f1 = pow 2 f2 = pow _ 2 This makes it look like a lambda but without having to bind an argument explicitly. Makes it much nicer to write complicated expressions (although I can't speak to the readability for people less fluent in the syntax).
- randallsquared 9y agoWell, it isn't like you can avoid understanding B by using A: A adds a concept, but it doesn't remove any of the existing ones; it just makes you do B in your head when you see A to arrive at the concept which is reified in the function name. Even if you're only using it in one place, `atLeastTwo(some_value)` is simpler than `partial(Math.max, 2)(some_value)` in at least that way.
- avmich 9y agoI'd argue that they are the same. Calculating return value of B (return Math.max(2, someNumber)) is calling Math.max with two arguments, the second of which is explicitly named. The result is the same as partial(Math.max, 2)(someNumber). someNumber isn't used anywhere else, and the function itself just does the supplement of someNumber as second argument to Math.max . That's also what partial(Math.max, 2) does.
- MrBuddyCasino 9y agoI'd argue B is simpler. Why? Naming things is helpful. A function named "atLeastTwo()" is more or less self-explanatory, I don't have to look at the implementation to know what it does. In case of A, I have to look at the implementation and figure out what it does. A is maybe more _elegant_, but IMHO it has a higher mental load.
- delibes 9y agoI see what you're getting at with (B), since you've named the function which adds another 'thing' to think about. But I can anonymise it in modern Javascript: y = (x) => {return Math.max(2, x)} To me that just looks nice, and it's clear how Math.max is being used. With the partial (A) I have to have some implicit knowledge of the function, where as in (B) or my version I can see how it's being used. Perhaps I'm a bit dis-functional.
- lmm 9y agoI mean, => is standard in the language and partial isn't, but that's an accident of history. Your example looks reasonably nice, but the "x" isn't really adding anything; if we could somehow write it as y = => {Math.max , 2} that would be clearer still (setting aside familiarity with the language). Whether "=>" or "partial" is a better name is a separate question.
- sidlls 9y agoIt's not objectively simpler at all, actually. It does not express anything with objectively fewer concepts. Instead it distributes the concepts differently.
- mikeash 9y agoSeems to me that A has two major concepts (function calls and partial application) whereas A only has one (function calls).
- jasonkester 9y agoA is objectively more complex: B gives you all the information you need to know what's going on. A requires you to go dig up what "partial" is doing (which, it turns out is itself doing some wacky gymnastics to turn one of its arguments into a function and the other into a parameter (and the parameter of whatever gets passed into the returned function as the other parameter of the function that was originally passed in). Observe how much writing it took just to describe what's going on with your "A". No explanation at all is required for B. It's complete as written. Simple, and intuitive all in one.
- beaconstudios 9y agoif you're writing functional code, you should already know what partial application is - and the "partial" function is only necessary in this case because JS isn't a curried language by default. your idea of B being more simple is largely driven by the fact that you likely have more experience writing code in that manner. With experience writing functional code you should find that both A and B are clear at a glance.
- avmich 9y ago"partial" is an independent concept, which could be learned once and applied after that just as "function" can be in example B. You don't say that "function" and "return" are doing some wacky gymnastics in the code do you? It takes a lot to describe what's going on with A because it's a relatively new concept, related to the subject of the article. If partial application would be just as well known as function definition, the explanation could be shorter.
- jasonkester 9y agoBut it's not. "partial" is specifically defined as its own function in the article. If you're a developer coming across this guy's project for the first time, you'll have to dig one level further into the stack before you find it. Only then do you get to learn those unnecessary gymnastics, so you can follow the rest of his code.
- 9y ago
- gpvos 9y agoI'm not convinced. Please enumerate the concepts in both, the way you see it.
- mannykannot 9y ago> What we're interested in is simplicity. What we are really interested in is whether it makes programs easier to understand, less easy to misunderstand, and less likely to contain errors. A measure that is more objective but doesn't address these issues is not intrinsically better.
- tzs 9y ago> This (A): partial(Math.max, 2) > is objectively simpler than this (B): function atLeastTwo(someNumber) { return Math.max(2, someNumber); } What the article failed to convey to me was why either of these are better than just using Math.max(2, <expression>) everywhere I need to use the greater of 2 and some expression. It didn't really explain why we want to hide the 2 at the places where we need to use 2 as a lower bound on a value.
- Jtsummers 9y agoWhy would you want to do this? For exactly the same reason we don't want to see so-called "magic numbers" throughout our code. An equivalent question might be: Why should I use MAX_INT16 instead of 32767 everywhere? They encode precisely the same thing, but one is just a number and the other offers a meaningful name as well as one location where it's defined, reducing the risk of error. Or maybe you really want MAX_INTEGER which will be determined by your hardware platform, again you only have one place to redefine the value and not throughout your codebase. Maybe 32767 was selected in some places because it was the system's MAX_INTEGER, and in others it has some different meaning or reason for being used that shouldn't be altered by changing MAX_INTEGER. Back to atLeastTwo. It's a trivial example, but consider if you actually did have Math.max(2, e) throughout your code base. What is the meaning of this expression? Gives you either 2 or e whichever is greater. Great, we understand what it is. But now, why? What is magic about 2. Giving it a name can convey the why (though atLeastTwo does not). Giving it a name also makes it less error-prone. Suppose you fat-fingered 22 into a couple spots, how do you (quickly and easily) identify that error? If it's just Math.max(22, <expression>) you can't even know whether it's wrong, just that it has a 22 in it instead of a 2. Sufficient testing would (hopefully) reveal that you used 22 instead of 2, but it may lead to subtle bugs that are hard to identify. We have this issue in a codebase at work where we do a lot of bit twiddling. Instead of setXFlag(&data_word, val) we have something like setBits(7,1,&data_word,val). That 7,1 determines which bit to start at (7) and how many (1) to put val into. Great, it works. But when the data format got changed we had to change every single reference to 7,1 to 6,1. Unfortunately it was really, really fucking hard to find all those references and correctly discern which ones needed to be changed and which ones didn't.