
When Does Point-Free Notation Make Code More Readable? - philk10
https://spin.atomicobject.com/2017/09/29/point-free-notation/#.Wc5PtNDfhoc.hackernews
======
ibejoeb
Okay, I get the concept. Where is the argument for it?

>Sometimes, especially in abstract situations involving higher-order
functions, providing names for tangential arguments can cloud the mathematical
concepts underlying what you’re doing.

Sometimes? When?

>point-free notation serves as an example of how subtle changes to the way you
think about or define your code can have big impacts on readability.

In the end, we're defining `atLeastTwo` and using it identically, so it would
be much more compelling if there was an example in which things were, in fact,
more readable. This example is just syntactic. It's like arguing that `const
atLeastTwo = (x) => x >= 2` is better than `function atLeastTwo(x) { ... }`

> It’s your job as a developer to be cognizant of these tradeoffs.

It's just that this piece isn't really helping me get there.

I think the author is suggesting that we define `atLeastTwo` in terms of an
`atLeast` primitive, i.e., currying and partial application. It's clear that
this is going to make composition easier, but it is relying on it. Now, I'd
like to see how point-free, as a paradigm, yields better results in terms of
readability or error prevention.

~~~
vanderZwan
Concatenative languages come to mind. You basically don't have to think about
partial application ever again, because you get it "for free" (at the cost of
reverse Polish notation). Forth, Joy, Factor and Kitten are all examples of
languages based on this paradigm.

[0]
[https://en.wikipedia.org/wiki/Forth_(programming_language)](https://en.wikipedia.org/wiki/Forth_\(programming_language\))

[1]
[https://web.archive.org/web/20111007025556/http://www.latrob...](https://web.archive.org/web/20111007025556/http://www.latrobe.edu.au/phimvt/joy/j02maf.html)

[2] [http://factorcode.org/](http://factorcode.org/)

[3] [http://kittenlang.org/](http://kittenlang.org/)

~~~
hashmal
Indeed, concatenative languages are a perfect example of point-free notation.
I don't see the point (pun!) of doing it in JS or any mainstream language
though.

They are also a joy to design/implement* and doing it really changes how you
think about programming.

* [http://hashmal.github.io/shirka/](http://hashmal.github.io/shirka/) my personnal attempt at designing such a language. warning: very old, dirty, buggy code.

------
freefal67
"JavaScript doesn’t include this out of the box, but it’s an interesting
exercise to write your own."

I may be misunderstanding point-free notation (I had never heard of it
before), but I think `bind` ([https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)) let's a
programmer provide some of the arguments to a function and returns a new
function that requires only the remaining arguments.

~~~
Too
It's also easy to implement as a one liner

    
    
       var atleastTwo = function(x){return function(){return Math.max(2,x);}
    

Even simpler with typescript

    
    
       var atLeastTwo = (x) => (() => Math.max(2, x))

~~~
WalterSear
ES6

    
    
        const atLeastTwo = x => Math.max(2,x)

~~~
leshow
his is a function that returns a function, i.e. you'd need to call
atLeastTwo(x)(); to get a result, yours is just a function. in es6 the
equivalent thing to what he wrote would be:

    
    
        const atLeastTwo = x => () => Math.max(2, x);

------
dreamcompiler
Why do we need yet another term for concepts already known as function
composition, currying, and partial application?

~~~
mrkgnao
Pointfree style _uses_ composition (among other operators), currying, and
partially applied functions, but it refers to something distinct from the sum
of those concepts/features.

Consider the following function of two parameters, written in
Haskell/PureScript syntax:

    
    
       f x y =  g x (m (h y))
             = (g x . m . h) y
    

We can now eta-reduce/partially apply f instead in the definition (which is
possible because f is curried by default):

    
    
       f x   = g x . m . h
    

If I were using pointfree style at all (which I almost never do), this is
where I'd stop. However, you can go the whole hog and strip out all the
arguments:

    
    
             = g x . (m . h)
             = ((. (m . h)) . g) x
    
       f     = (. (m . h)) . g
    

I've made use of everything you listed, but it's only when you go this far
that your function is truly free of "points".

~~~
lr4444lr
_Pointfree style uses composition (among other operators), currying, and
partially applied functions, but it refers to something distinct from the sum
of those concepts /features_

Exactly. But in all fairness to the parent comment to yours, the original
article doesn't convey this as well as you lucidly did, and his confusion is
perfectly understandable.

~~~
mrkgnao
Thanks! I'd say at least some of the blame for that is due to how C-family
languages make these concepts appear somewhat ... opaque.

------
barrkel
Point-free notation builds a program indirectly using expressions, rather than
declaratively and directly like most programming languages. You need to follow
data and control flow in order to understand the program that is built.

I think it's rarely easier to read or understand, with a few major exceptions.
Certainly toy examples like in the article are too simple to highlight the
benefits.

If the blocks being composed are reused and recombined a lot, but are meaty
enough not to be inlined, then the composition effectively turn into a kind of
high-level DSL. Consider e.g. a data processing pipeline using relational
operators; filters, joins, sorts, projections etc. Those things have meaty
implementations, but fairly simple interfaces; they're tailor-made for
composing into larger abstractions that have the same external interfaces.

If the problem being solved inherently requires building a custom program to
solve it, then it's hard to avoid writing something that isn't isomorphic to
point-free style. For example, a reporting tool that doesn't dump the user in
a SQL editor.

------
Etheryte
I don't really find the argument convincing. I'd argue that the original
approach was more intuitive.

~~~
wellpast
"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.

~~~
jasonkester
A 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.

~~~
avmich
"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
But 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.

~~~
lmm
If "partial" were only usable for this one function you'd be right. But
actually it's a very common, general function (under various names); in many
languages it's already present in the standard library, or at least in an
established library that's commonly used.

Fundamentally this is what programming is all about - finding the commonality
that you can pull out and reuse, rather than doing every single case by hand.

------
danharaj
Point-free isn't going to be worth it in a language where composition and
application of functions isn't syntactically and computationally as cheap as
possible. It also only works perfectly well for unary functions and
situationally for binary functions. Anything more and the contortions start
being more unreadable than variables.

In Haskell partial application is literally free and composition is an
operator. That's why point-free works well there. No point in importing
syntactic ideas that don't make sense in the new context.

Edit: I should also point out there's a subtle corner-case if you mix these
transformations with uses of `seq`.

------
falcolas
So, you get a single point-free function, at the cost of two other point-full
functions (at least one of which will utilize run-time introspection)? All
you're doing is pushing the "point" somewhere else - in this case down onto
Math.max.

Seems like a net loss of simplicity and readability.

~~~
savanaly
That's only true if you never reuse Math.max. I believe the philosophy the OP
is espousing is to write a few point-ful funcitons when necessary and then
make the bulk of your business logic be recombinations of those in a point-
free way, rather than redoing the points at every function the whole program
wide.

I will agree it is a big loss of readability for someone who's not used to
reading programs that are written like that. And that includes me. But I have
faith it will get easier to read and eventually I will find it easier to read
code written in that style than code written in the old style.

------
harunurhan
I disagree, I don't know may be it's a preference thing but I think the code
with points is readable for me and doesn't require to write extra function,
class etc.

~~~
avmich
A hallmark example of point-free style in J is likely the example of
calculating the average value of the array:

+/ % #

Here literally you say "I'm calculating sum of elements (+/) which then divide
(%) into the number of elements (#)".

If your array isn't just a one variable (x) but instead is obtained as a
result of an expression, you may not want to repeat that expression twice or
to invent an intermediate variable to store the array result. Here point-free
style can help.

------
mywittyname
Composition and currying are great if you're the only person working on a
project, because you will understand everything that's going on. But my
experience has been that developers will not inherently come to the same
conclusions with this style, using this on a team of more than two leads to
project fragility and incongruousness.

------
ahayter
A couple of posts that really made this click for me (just beginning my
programming career). This is javascript library, but the concepts do translate
functional programming in general.

[http://fr.umio.us/why-ramda/](http://fr.umio.us/why-ramda/)

[http://randycoulman.com/blog/2016/05/24/thinking-in-ramda-
ge...](http://randycoulman.com/blog/2016/05/24/thinking-in-ramda-getting-
started/)

(Read all posts from the second link)

After reading this series of blog posts and using Ramda.js for the last year
or so, I can see the value here. But you do have to "get it" which means
learning additional concepts, if the additional learning isn't a burden to
your team (I hope your team isn't averse to learning new concepts) then I
think it's an asset to be used when it makes sense.

------
beaconstudios
given the article neglects to provide an example, I have one handy from my
git:

[https://gist.github.com/ajeffrey/092b71a3ad5f2034601574262c5...](https://gist.github.com/ajeffrey/092b71a3ad5f2034601574262c5c1167#file-
cgol-js-L26)

Note how on this line, arguments are not present and passed around - the input
of the function being declared is simply passed from function to function,
with the output of each becoming the input of the next. When you can write
code this way, it improves clarity by putting the focus on what the code is
_doing_ rather than whatever arbitrary name you're giving the input
parameters. It's definitely a tool for use in specific situations though as
often the name of the arguments helps a great deal with readability.

------
ape4
So point-free avoids the parameters. Why not call it parameter-free. Or
argument-free.

~~~
lmm
The concept originated in topology, where the parameters to your functions are
points (in a space).

------
chubot
FWIW I wrote a related blog post:

"Shell Pipelines Support Vectorized, Point-Free, and Imperative Style"

[http://www.oilshell.org/blog/2017/01/15.html](http://www.oilshell.org/blog/2017/01/15.html)

I'm also writing a blog post right now that uses this real instance of point-
free style:

    
    
        count-nul() {
            od -A n -t x1 | grep -o '00' | wc -l  
        }
        # usage: count-nul < myfile.bin
    

The issue is that grep can't find NUL bytes, because argv is NUL-terminated.
So you have to convert to hex, grep, and then count.

------
habitue
We use point free notation in JavaScript most often with "fluent" apis like d3
or underscore, where instead of using an explicit compose operator like in
haskell, each method in a chain returns the context with a ton of chainable
methods defined on it.

The big difference is that Haskell (and other ML-syntax languages) make
currying the default so it's much less noisy syntactically to use pointfree
style than it is in JavaScript.

[https://wiki.haskell.org/Pointfree](https://wiki.haskell.org/Pointfree)

------
ArchReaper
This seems like a great way to obfuscate all your code without any actual
benefit. Can anyone give me a real example of how this is somehow a better way
to do things? Maybe the application for this is only for a specific field that
I'm not familiar with?

~~~
mrkgnao
Working with streams of data often makes pointfree syntax beneficial, since
you can start to see the omitted arguments "flow" through your chain of
composed functions. If you write

    
    
       f # g = \x -> g (f x)
    

then you could implement functions like this:

    
    
       f :: Stream Int -> Stream Int
       f = drop 3       -- drop the first three elements
         # skipEvery 3  -- skip every third element
         # sumEvery 2   -- sum every consecutive pair of elements
         # map (* 2)    -- double everything
    

This would turn

    
    
       1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...
    

into

    
    
       2 * (4 + 5), 2 * (7 + 8), 2 * (10 + 11), ...
    

You can find many _non-pointfree_ versions of similar streaming code here:
[https://github.com/snoyberg/conduit/](https://github.com/snoyberg/conduit/)

------
dfboyd
From a Clojure standpoint, I don't think there's much going on here beyond
comparing this:

    
    
      (defn at-least-two [x] (max x 2))
    

with this:

    
    
      (def at-least-two (partial max 2))

