6 ms·
> the only difference is whether or not it's hidden behind function calls you can't see and don't have access to. You "can't see and don't have access to" `if`
by stouset 2y ago
> the only difference is whether or not it's hidden behind function calls you can't see and don't have access to.
You "can't see and don't have access to" `if`, `range`, or `append` but somehow you don't find this a problem at all. I wonder why not?
> You don't really think that functional languages aren't appending things, using temp vars, and using conditional logic behind the scenes, do you?
By this metric all languages that compile down to machine instructions are equivalent. After all, it winds up in the same registers and a bunch of CMP, MOV, JMP, and so on.
`.distinct()` could sort the result and look for consecutive entries, it could build up a set internally, it could use a hashmap, or any one of a million other approaches. It can even probe the size of the array to pick the performance-optimal approach. I don't have to care.
> [".filter(node => !node.isHidden)" is] nothing but a for loop and a conditional by another name and wrapped in an awkward, unwieldy package.
This is honestly an absurd take. I truly have no other words for it. map, filter, and friends are quite literally some of the clearest and most ergonomic abstractions ever devised.
- desumeku 2y ago> it could build up a set internally, it could use a hashmap, or any one of a million other approaches. It can even probe the size of the array to pick the performance-optimal approach. I don't have to care. Well, this is probably why functional programming doesn't see a lot of real use in production environments. Usually, you actually do have to care. Talk about noticing a performance regression because I was simply appending to an array. You have no idea what performance regressions are happening in ANY line of FP code, and on top of that, most FP languages are dead-set on "immutability" which simply means creating copies of objects wherever you possibly can... (instead of thinking about when it makes sense and how to be performant about it)
- torben-friis 2y ago>Well, this is probably why functional programming doesn't see a lot of real use in production environments The usual map/filter/reduce is everywhere in production. Python, java, js, ruby, c#... You could even argue that lack of generics hurt Go's popularity for a while precisely for that usecase.
- whilenot-dev 2y agoI consider an abstraction to be a good abstraction if I don't need to care for its internal workings all the time - whether that's some build step in the CI, or the usage of data structures from simple strings to advanced Cuckoo filters and beyond. Even Python uses a Bloom filter with operations on strings internally AFAIK. Correctness and maintainability trumps performance most of the time, and map, filter and immutability are building blocks to achieve just that. Those constructs won't prevent you from looking deeper and doing something different if you really have to (and know what you're doing), they just acknowledge that you'll probably don't need to do that.
- desumeku 2y ago> Correctness and maintainability trumps performance Great, please make all the software even slower than it already is. I am overjoyed to have to purchase several new laptops a decade because they become e-waste purely due to the degradation of software performance. It is beyond ridiculous that to FP programmers daring to mutate variables or fine-tune a for loop is an exceptional scenario that you don't do "unless you have to" and which requires "knowing what you're doing". Do you know what you're doing? How can you be a software engineer and think that for loops are too difficult of a construct, and that you need something higher-level and more abstract to feel safe? It's insane. Utterly insane. Perhaps even the root of all evil, Code Golf manifested as religion.
- stouset 2y ago> Great, please make all the software even slower than it already is. It is more or less trivial to emit essentially optimal autovectorized and inlined machine code from functional iterators. Rust does this, for example.
- deleted 2y ago[deleted]
- alpaca128 2y agoYour assumption that filter/map/reduce is necessarily slower than a carefully handcrafted loop is wrong, though. Rust supports these features as well and the performance is equivalent. Also countless real-world production environments run on Python, Ruby, JS etc, all of which are significantly slower than a compiled FP program using filter & map. > FP languages are dead-set on "immutability" which simply means creating copies of objects Incorrect. The compiler can make it mutable for better performance, and that gives you the best of both worlds: immutability where a fallible human is involved, and mutability where it matters.
- desumeku 2y ago> The compiler can make it mutable for better performance Well, we already know that no pure FP language can match the performance of a dirty normal imperative language, except for Common Lisp (which I am happy to hear an explanation for how it manages to be much faster than the rest, maybe it's due to the for loops?). And another comment here already mentioned how those "significantly slower" scripting languages have a healthy dose of FP constructs -- which are normally considered anti-patterns, for good reason. The only language that competes in speed in Rust, which just so happens to let you have fast FP abstractions so long as you manually manage every piece of memory and its lifetime, constantly negotiating with the compiler in the process, thereby giving up any of the convenience benefits you actually get from FP.
- maleldil 2y agoYou complain about not having control of how the `filter` works under the hood but are happy to give the language control over memory management. How much abstraction is too much abstraction? Where do you draw the line?
- DonHopkins 2y agoSpeaking of filters and clear ergonomic abstractions, if you like programming languages with keyword pairs like if/fi, for/rof, while/elihw, goto/otog, you will LOVE the cabkwards covabulary of cepstral quefrency alanysis, invented in 1963 by B. P. Bogert, M. J. Healy, and J. W. Tukey: cepstrum: inverse spectrum lifter: inverse filter saphe: inverse phase quefrency alanysis: inverse frequency analysis gisnal orpcessing: inverse signal processing https://en.wikipedia.org/wiki/Cepstrum https://en.wikipedia.org/wiki/Cepstrum
- porridgeraisin 2y ago> by this metric False equivalence. You're saying that the statement "both for.. append and .map() executing the _same steps_ in the _same order_ are the same" is equivalent to saying that "two statements being composed of cmp,jmp, etc (in totally different ways) are the same" That is a dishonest argument. > Distinct could sort and look for consecutive, it could use a hashmap People love happy theories like this, but find me one major implementation that does this. For example, here is the distinct() implementation in the most abstraction-happy language that I know - C# https://github.com/dotnet/runtime/blob/main/src%2Flibraries%2FSystem.Linq%2Fsrc%2FSystem%2FLinq%2FDistinct.cs https://github.com/dotnet/runtime/blob/main/src%2Flibraries%... It unconditionally uses a hashset regardless of input. Edit: found an example which does different things depending on input https://github.com/php/php-src/blob/master/ext/standard/array.c https://github.com/php/php-src/blob/master/ext/standard/arra... This does the usual hashset based approach only if the array has strings. Otherwise, it gets the comparator and does a sort | uniq. So, you get a needless O(nlogn), without having a way to distinct() by say, an `id` field in your objects. Very ergonomic... On the other hand... seen := map[string]bool{} uniq := []string{} for _, s := range my_strings { if !seen[s] { uniq = append(uniq, s) seen[s] = true; } } Let us say you want to refactor and store a struct instead of just a string. The code would change to... seen_ids := map[string]bool{} uniq := []MyObject{} for _, o := range my_objects { if !seen_ids[o.id] { uniq = append(uniq, o) seen_ids[o.id] = true; } } Visually, it is basically the same, with clear visual messaging of what has changed. And as a bonus, it isn't incurring a random performance degradation. Edit 2: An SO question for how to do array_unique for objects in php. Some very ergonomic choices there... https://stackoverflow.com/questions/2426557/array-unique-for-objects https://stackoverflow.com/questions/2426557/array-unique-for...
- sfn42 2y ago> It unconditionally uses a hashset regardless of input. This follows what I like to call macro optimization. I don't care if a hash set is slightly slower for small inputs. Small inputs are negligibly fast anyway. The hash set solution just works for almost any dataset. Either it's too small to give a crap, midsized and hash set is best, or huge and hash set is still pretty much best. That's why we default to the best time/space complexity. Once in a blue moon someone might have an obscure usecase where micro optimizing for small datasets makes sense somehow, for the vast majority of use cases this solution is best.