In this lesson, we’ll refactor a series of function calls into a functional pipeline, making the code more declarative and removing the need for intermediate variables. Rather than reaching for a utility library, we’ll start by building our own pipe function to gain a clear understanding of how left-to-right function composition is accomplished in JavaScript.
poah this one is hard to follow :), especially that (...args) fn in the middle destroys all my theories about how that could work. is there a way to visualize this reduce iteration so see each iterator outcomes? that'd be awesome!
I have a written version of this that might help clear it up:
https://vanslaars.io/post/create-pipe-function/
Hope this helps!
this totally helped out, awesome blog post including exactly the information to go the very last mile understanding this :). thx!
Awesome! Glad it helped :)
Andrew,
Hi this is Patrick again. Quick question about the pipe function (and its mostly academic, not really about the functionality). Anyway, when pipe is called by getUpdatedTodos, something curious happens. If you console.log the functions coming in via the ...fns operator, you will see, as you would expect, an array of 3 functions --> findById, toggleTodo and the bound updateTodo. No problem. But whenever I ask to see the function's name property, I get an error for bound updateTodo. That is, if I do a try/catch block like so:
try { console.log(...fns.name) } catch(e) { console.log(e.message); }
I get findById, toggleTodo and an error (can't convert undefined or null to object).
My first thought was this was because of the bound nature of the updateTodo. But it does seem to have a name just like the other two. Granted, it has 'bound' in front of it, but there doesn't seem to any reason why I shouldn't still be able to access its name. I can access it from the partial function if I separate the return function into a variable first:
export const partial = function(fn, ...args) { console.info("Remember...fn.bind() is not calling anything - it is merely binding. The fact that it has arguments is just binding the arguments..."); let fnBind = fn.bind(null, ...args); console.log(fnBind.name+" --> fnBind.name"); // bound updateTodo return fnBind };
Any idea why this is the case? Again, purely academic, but I am curious...thanks again!
p.s. one thought is that it has something to do with babel...if you console.log out the functions themselves, you will notice they are written like this: function findById(id, list), function toggleTodo(todo), and function (). The last function is of course the bound one. Might it be that rather than look in the function object itself, they are just parsing the text - if any - in front of the word function? In other words, if it is blank, it is presumed to not have a name (even though we know it is -- just bound). Anyway...
Patrick,
That is an interesting question. I'm not sure why you're seeing that behavior, but I'm also not entirely sure where you are trying to access the function name in the code. If you want to put a complete sample in codepen or plunker that recreates the issue I'd be happy to take a look.
Andrew, I really liked the elegance of the pipe together with the partial.
However I was wondering how it would play with the IDE prompting you for input parameters as most devs would rely on the IDE to prompt for the function parameters.
Is there a particular approach or style you use when using pipe to benefit from the IDE's prompting of the signature, e.g. is it inner to out or vice verca or do you write it out longhand in separate lines, and then apply the pipe later as a refactor?
Also, the newly created piped composite function will not be known to the IDE, and so when using it you will not get any parameter prompts which could lead to a possible error. Sorry if this sounds being critical, but just wondering how you overcome these in day to day development.
Hi Andrew, There is a small typo in the transcript: pipe = (f, g) => {...args} => g(f(...args)) Must be: pipe = (f, g) => (...args) => g(f(...args)) :)
I like the clean of the pipe solution but my concern is about debugging. Remember the saying: "Write simple code like if the maintainer is crazy guy that knows your address"
Great tutorials so far! Unfortunately, I'm going to opt to leave the code as is without the pipe function. I find it incredibly hard to follow and the cognitive complexity is through the roof for something that should be very straightforward... Elegant isn't always better; simple rules when it comes to code both for your future self and others trying to interpret and maintain your code.
Great tutorials so far! Unfortunately, I'm going to opt to leave the code as is without the pipe function. I find it incredibly hard to follow and the cognitive complexity is through the roof for something that should be very straightforward... Elegant isn't always better; simple rules when it comes to code both for your future self and others trying to interpret and maintain your code.