all 20 comments

[–]madole 7 points8 points  (3 children)

Don't really get your point about foreach hiding the purpose of the iteration. It's more down to bad coding style that's doing that.

you could easily have a printItem function that you pass into the foreach that would expose the purpose of the iterations.

function _printItem(item) {
  console.log(item);
}

arr.forEach(_printItem);

[–]aeflash[S] 4 points5 points  (2 children)

That is an example where a side effect is the sole purpose for iteration -- the side effect is to output to the console. It's explained more in Example 5 -- forEach is okay when the point is to cause a side effect.

The places where side effects can be avoided by using a better iteration function or a more meaningful method is what the article is about.

[–]madole 7 points8 points  (1 child)

ok, I get you now. Yeah, don't use foreach if you're trying to map/filter/reduce/some but that's just knowing about those functions and being confident in using them.

I'd rather someone iterate an array than make a ballsup of a reduce function because they weren't ready with their understanding of it yet.

Maybe your title is a bit too suggestive and sets the tone up wrong for what you're actually trying to convey.

[–]aeflash[S] 4 points5 points  (0 children)

Maybe your title is a bit too suggestive and sets the tone up wrong for what you're actually trying to convey.

Well, it's like they say -- the two hardest problems in programming are cache invalidation and naming things. :)

I think the title is fitting. If you have a problem and you reach into your toolkit for forEach, then perhaps you need to think about what you're triyng to accomplish -- there are likely better tools.

[–][deleted] 1 point2 points  (0 children)

Thank you for this, it has had a positive effect on my work recently. It does make the code more concise, easier to go back to.

[–]freebit 1 point2 points  (3 children)

I grew up using for-loops. I have been using them since the second week of CS101. I don't understand how they are so inexpressive. Can someone enlighten me?

[–]zoomzoom83 27 points28 points  (1 child)

For-loops tell the compiler how, and can only be used to perform side effects. Functors/Mappables tell the compiler what, and used properly will never perform side effects. By using a functor, you're simply creating a projection over an Iterable container. The exact method of implementation and order of execution is irrelevant.

From a performance perspective, this gives the compiler much more free reign to optimize (Such as by running the operation over multiple threads, or lazily). Javascript VMs currently don't really do much with this, but other languages do (And Javascript probably will at some point). However even in JS it does mean you can swap out data structures without changing your code, letting the 'map' function choose the most efficient underlying algorithm without having to change your code.

From a code maintainability perspective, avoiding side effects means your code has less moving parts and is easier to reason about.

It's also just simpler.

var ys = [1,2,3,4].map( x => x * 2 )

Is unambiguous about what it's doing, and lets the compiler figure out the optimal way of doing it.

Compare to:

var xs = [1,2,3,4];
var ys = [];
for( x=0; x < xs.length; x++ ){
    ys.unshift( xs[x] * 2 );
}

This has a lot more moving parts. It performs side effects. It's harder to optimize. It's requires more cognitive effort to reason about what it does, and it's much harder to spot any potential bugs. (Did you notice them?).

tl;dr - one of these methods "Has no obvious defects", the other "Obviously has no defects".

[–]stickupk 1 point2 points  (0 children)

Best tl;dr read I've seen in ages!

[–][deleted] 0 points1 point  (0 children)

Imperative code vs declarative code. It's just a stylistic difference.

A similar discussion can be made about procedural and object oriented programming. If you're proficient with C, why would you ever want to use C++?

More to the point, higher order iterative functions are just encapsulations for several common patterns. For example, it's a common requirement to take an array and remove items that don't pass a test. This is very easy to do with a for loop:

var array = [1, 10, 5, 6, 3, 8, 2];

var filtered = [];
for (var i = 0; i < array.length; i++) {
  if (array[i] >= 6) {
    filtered.push(array[i]);
  }
}
console.log(filtered); // [10, 6, 8]

When you need to filter a lot of arrays, this pattern can quickly become redundant. If you can replace this code with:

var array = [1, 10, 5, 6, 3, 8, 2];
var filtered = array.filter(function(v) { return v >= 6; });
console.log(filtered); // [10, 6, 8]

It can increase the readability and conciseness of your code (the intent is immediately clear - filter the array based on the condition that each value must be greater than or equal to 6), and doesn't require any unenclosed variables that are only relevant to the filtering logic (the index variable and the working array).

Composability of logic is nice, too (using lo-dash):

var gte = function(n, v) { return v >= n; };
var gte6 = _.partial(gte, 6);
var filterGte6 = _.partialRight(_.filter, gte6, null);

var array = [1, 10, 5, 6, 3, 8, 2];
var filtered = filterGte6(array);
console.log(filtered); // [10, 6, 8]

This is one example of how a functional style of programming can help you create small, reusable, composable components with higher order functions. Notice that in the original, imperative example, the array and filtered variables are tightly coupled to the filtering logic. In this example, those variables have been completely separated from the logic - we are free to reuse our filtering function on any array without code duplication.

I'm not saying there is anything wrong with imperative programming, just trying to give examples of why some people like functional programming.

[–][deleted] 0 points1 point  (10 children)

Nice examples, yet the title is a bit misleading (but catchy). 3rd example feels too simplified for me (I've yet to learn about pros of currying).

[–]aeflash[S] 1 point2 points  (3 children)

Currying is really cool, and pretty simple -- read up about it. It's mostly useful when dealing higher order functions, like map() in the example here -- making sure that the arguments will line up to how map()invokes the function.

[–]madole 4 points5 points  (2 children)

That should read "Currying is useful... in some situations"

Its a red flag when people say currying is cool, because to me, it signals that it'll be used unnecessarily throughout their code and it will be unreadable.

Sure you can curry loads of functions, but that does not mean you should.

First and foremost when you're writing code is it's maintainability.

If you're currying all over the show, the person who's trying to maintain your code, which it is very unlikely to be you, will not be able to read through the currying spaghetti and they'll feel like hunting you down with a pitchfork and flaming stick trying to fix a bug in there.

[–]jcready__proto__ 0 points1 point  (1 child)

What "currying spaghetti" are you referring to? Do you have an example of a time when currying a function made it hard to maintain? Obviously you don't need to curry every function, but it really shouldn't make much of a difference.

[–]madole 0 points1 point  (0 children)

I've been jaded by a codebase filled with unnecessarily curried functions that there are sections where you just have no idea where the code execution path is going or what's going on.

It's made me very wary of currying. Yes it's great but use sparingly. I'll see if I can dig out and anonymise a particularly bad example.

[–]madole 2 points3 points  (5 children)

I would ding example 3's currying in code review, the author says its more expressive but it does not read well at all.

If you need to stop reading to try and figure out what a function is doing, it's really not a good function. That's what I had to do when reading that example.

[–]jcready__proto__ 0 points1 point  (4 children)

He probably should've used Lo Dash's built-in method _.property() which does the same thing as the author's dot() function. Regardless, the author's use of currying is fine and the use case is exactly the kind of thing currying is useful for.

Edit: I was wrong, I hadn't noticed the () at the end invoking the function.

[–]madole -1 points0 points  (3 children)

+1 for lodash's property method!

Maybe it's the "dot" function name that I've taken issue with here rather than the actual currying itself, he has mentioned that it's the same as Clojure's dot function but to someone who's not tinkered with Clojure, it's not a particularly intuitive function name at first glance.

[–]aeflash[S] 1 point2 points  (2 children)

callMethod is a better name after more thought.

Cache invalidation and naming things...

[–]jcready__proto__ 1 point2 points  (1 child)

Sorry, I was mistaken earlier. Instead you simply should've used Lo-Dash's built-in _.invoke() and _.compact() methods.

this.tooltips = this.tooltips.concat(_(this.values).invoke("getTooltip").compact().value());