4 ms·
Over the past few years there's been a tendency towards a more functional style of programming, like what's demonstrated in this blog post. I think there are a
by jeffreyrogers 11y ago
Over the past few years there's been a tendency towards a more functional style of programming, like what's demonstrated in this blog post. I think there are a lot of benefits to this, but the functional style doesn't hold a monopoly on good code.
One of the advantages of the "slightly better", more imperative, method of finding a 3D piece under the mouse over the "beautiful", more functional, one is that it is extremely clear what the code is doing, while the functional case needs some thought to understand; even for someone who knows what map, filter, etc. do!
This isn't to say that functional is bad and imperative is good, or really to choose a style at all. But sometimes the cleanest idea mathematically isn't the best way to write code.
- oisdk 11y agoReally? For me, a chain of maps and filters is far easier to understand than nested for-loops or something. I think it's a lot to do with exposure: imperative code is far more well-known, and most people are more exposed to it than they are to more declarative stuff. I'd say that if someone was exposed to both equally they'd find the declarative easier to quickly scan and understand.
- V-2 11y agoI agree. I'm an Android developer, but I have a knack for FP-flavored programming style - you get some of it in C# (LINQ), Kotlin, even Java (Streams, or Guava), and I find it readable. I never wrote a line of code in Swift, and the examples from the article were quite clear to me, too. Of course you can create an unreadable monstrocity with maps, filters, group-bys etc. but it's possible in goodd ol' OOP just the same. Every construct can be used right or abused.
- ska 11y agoone is that it is extremely clear what the code is doing, while the functional case needs some thought to understand; In my experience this is just familiarity, and I've experienced it in both directions.
- jimbokun 11y ago"One of the advantages of the "slightly better", more imperative, method of finding a 3D piece under the mouse over the "beautiful", more functional, one is that it is extremely clear what the code is doing, while the functional case needs some thought to understand; even for someone who knows what map, filter, etc. do!" This is not at all self evident. See map and immediately think "oh, this will invoke the function once for each item and collect the results". See filter and think "oh, this will invoke the function once for each item and collect the ones for which the function returned true". What about that is not extremely clear?
- sanderjd 11y agoI find this sort of functional-style code much easier to skim, because there are more obvious keywords with immediate connotations, rather than each line of code potentially doing something bespoke.
- RodgerTheGreat 11y agoImperative code is more explicit, but higher-level loop abstractions like map and filter are more restrictive and thus both easier to reason about and less error prone. When you program with functional patterns you remove whole classes of possible mistakes (or, when reading code, overlooked subtleties) surrounding the explicit mutation of index variables. The APL family eschews explicit "for" loops entirely in favor of functional forms and assigns special symbols to each of them. This results in extremely dense code, but at the same time code which often has fewer "moving parts" and is less likely to be subtly incorrect. In JavaScript I could write var sum=0; for(var x=0; x<100; x++) { sum += x; } or I could write var sum=range(100).reduce(function(a,b) { return a+b; }); (provided an appropriate definition of `range()`) In K I would write +/!100 The sum (+) over (/) the range 0 up to but not including (!) 100. In the first example, I have to worry about all sorts of boundary conditions and typos. The second example is easier to reason about, but it's fairly wordy. The third is so simple and concise it is "obviously correct". These examples are contrived, but I think they provide some food for thought.
- ta0967 11y agoif you were to program in JS in a style which calls for passing operations around you would have the operator `+` wrapped, so the second version would probably look like this var sum = range(100).reduce(0, plus); or rather var total = sum(range(100)); this does not take away from the beauty of APL.
- jlarocco 11y agoOne pitfall I often fall into with map, filter, etc. is that I chain them together, forgetting each of them takes another pass through the sequence. With an explicit loop, it's often easier to reduce multiple passes into a single pass.
- V-2 11y ago> each of them takes another pass through the sequence Not necessarily. In C# (LINQ) you have deferred execution / lazy evaluation, when possible of course - it's possible for filtering, not possible for a group-by, but that's just reality. Combine it with yield-return pattern, and it's very easy to avoid the problem you brought up.
- dragonwriter 11y ago> One pitfall I often fall into with map, filter, etc. is that I chain them together, forgetting each of them takes another pass through the sequence. Lazy maps, filters, etc. that do not do this are available in many languages (and the default in some.) Transducers provide a similar benefit, from a somewhat opposite direction (composing reduction functions rather than chaining lazy iterating functions). Other than familiarity for some people, I don't see a real edge to imperative iteration over functional operations here.
- jlarocco 11y agoI guess I should have been more clear. I'm aware that they do not add extra passes in some languages. But in Swift, C++, Common Lisp, OCaml, Python, Ruby, Perl, Java, Scheme, and many others, adding another map/filter/etc. will add another pass through the sequence unless you're going out of the way to avoid it.
- dragonwriter 11y ago> But in Swift, C++, Common Lisp, OCaml, Python, Ruby, Perl, Java, Scheme, and many others, adding another map/filter/etc. will add another pass through the sequence unless you're going out of the way to avoid it. I think this is wrong in the case of Python -- map/filter/reduce are lazy, and so chaining them does not result in multiple iterations of the source sequence. In Ruby, its definitely sometimes wrong, because whether or not iteration is lazy depends on the particular Enumerable object; and, in the worst case, in Ruby, you don't have to go very far out of your way. In the event that the Enumerable you are operating on isn't already a lazy one, you just call its lazy method, and chain your operations after that.
- rthomas6 11y agoAre you sure that it isn't just harder for you to understand because you're more familiar with the traditional imperative control flow tools? If you had spent as much time with map and filter as you likely have with for loops, maybe they would seem just as clear?