6 ms·
Edge case removed: for i=0 to 10 {print(a[i])} vs for x in a {print(x)} With functional programming it gets even better: a.filter {}.map{print} Pseudo co
by melling 7y ago
Edge case removed:
for i=0 to 10 {print(a[i])} vs for x in a {print(x)}
With functional programming it gets even better:
a.filter {}.map{print}
Pseudo code, of course. I list several “functional“ examples in Swift here:
https://github.com/melling/SwiftCookBook/blob/master/functional.md https://github.com/melling/SwiftCookBook/blob/master/functio...
Functional programming is also a higher level language. map, reduce,filter, flatten, flapMap, drop, take, zip, ...
Learn the concepts in one language and they can be used in another functional language.
- Allower 7y agoLast I checked functional programming doesn't have a monopoly on "map, reduce,filter, flatten, flapMap, drop, take, zip", these are functions and can be implemented in any language, object oriented or otherwise.
- dleslie 7y agoThe problem in my domain, games, is the frequent boxing and unboxing of values when using functional iterator interfaces in languages common to the industry, particulalry C#. It just thrashes the heap and forces often too-frequent gc.
- AndrewDucker 7y agoI was under the impression that the way C# did generics meant that there was no need to box values. (Unlike Java, which treats all values as objects, even value types.)
- jayd16 7y agoC# has value types and many things are value types but certain things are not. Core library interfaces are implemented as with Object enumerator types for example. This causes IList.GetEnumerator() to return a boxed type. Its smart enough to use the value type when it knows it can though, such as List.GetEnumerator(). Lambdas have some allocations. I like LINQ but it can have some allocation as well. These are all tiny allocations but in a game you'd like to strive for 0 allocations.
- hinkley 7y agoTwo ways to interpret boxing here: 1 Storing a primitive as a first class object 2 packaging and repackaging data There may be situations where the former is not an issue, but the latter often is. If you don’t have a way to treat filter() as a generator/coroutine it means your traversing one collection and inserting those values into another, only to pass it to map() which iterates again and creates a third. If you’re doing that once a frame, maybe not a big deal. Three times per object per frame? That’s a lot of temporary data structures. If the other responder is right, I speculate that the borrow checker forced someone’s hand in Rust and they found that a generator keeps the borrowed objects on the call stack where they already had tools to track ownership.
- pjc50 7y agoCorrect usage of IEnumerable helps a lot, as well as the magic that Linq can do with expressions.
- dleslie 7y agoIn my experience, the trick is to remove `using Linq` from the entire project.
- hinkley 7y agoAs someone who has had a mild case of Linq jealousy for some time, I’m curious what makes you averse.
- dleslie 7y agoIt's too easy to fall into the temporary collection trap you described; on a large project with many developers, allowing Linq is a bit like leaving a toaster plugged in beside the pool.
- miloignis 7y agoWhile it may or may not be optimal for you/your field for other reasons, I believe Rust does iterators in a 0-cost, no allocation way and generally generates code equivalent to or better than a hand-written for loop.
- dleslie 7y agoThere are studios moving to rust, and there definitely is a great amount of interest in rust among my peers because of the 0-cost iterator features. The borrow-checking isn't even a big draw, it's the other features!
- Mirioron 7y agoWhile it does remove edge cases, the code also becomes more opaque. At least to me, functional style programming seems much harder to read and then reason about.
- dpc_pw 7y agoIt takes some time to get used to and change mental model from "program describes step by step what to do" to "program describes logical flow of information". I moved from first camp to the second one somewhere during my adult life. And IMO: Both are valid, sometimes first one is necessary (because that's how the machines actually work), but second one is clearly better most of the time for humans, especially in business-logic heavy cases.
- thaumasiotes 7y agoThere's an analogous switch in algebra from notation like f(g(x)) to something more like X_{GF} (where the _{} indicates subscripting, not written symbols). Note the better left-to-right ordering in the terser system. It's the same dichotomy; are you thinking of f(x) as an instruction to do f to x, or are you thinking of X_F as what happens when X goes through F?
- vippy 7y agoThis is a learning thing, honestly. Once you start thinking in types, it clicks: you're thinking at a higher level of abstraction, so the code is less important than understanding how the programmer put the types together in their head to solve a problem.
- Mirioron 7y agoBut I am already thinking about what the code should do. The problem is that if I read functional code I get bogged down in the details of what limits are set by the operations, because they're usually not as explicit. It's like reading a chain of functions on some element (actually, it quite literally is that).
- lmkg 7y ago
- zyxzevn 7y agoA lot of those concepts were already in Smalltalk. a do: [:x| Transcript print: x ] [] = closure That is, because Smaltalk was based on Lisp. But the other OO languages removed this concept, but it came back in Scala.
- thewebcount 7y agoI program mainly in C++, and I often start off writing things as: for (auto x : someContainer) { //do something with x } But inevitably, I end up needing either an iterator pointing to a particular item at the end, or I need an index to pass to some other function, and this style makes that impossible. Perhaps it's just the domain I work in, but even after years of trying to make this work, it still only works about half the time for what I'm doing.
- eru 7y agoIn Haskell map someFunction listOfValue if you need an index: map someOtherFunction (zip [0..] listOfValues) Python uses 'enumerate' to similar effect. Not sure if C++ has an equivalent?
- a1369209993 7y agoYou can also use: zipWith someOtherFunction [0..] listOfValues # , since someOtherFunction is likely to be # :: (Int -> Value -> Stuff) rather than # :: ((Int,Value) -> Stuff) . zipIndex f = zipWith f [0..] # is also often useful.
- eru 7y agoYes. I wanted to keep things simpler for people who don't speak Haskell; and show off composition of simpler combinators to build more exciting things at the same time. In eg OCaml you have to use something like zipIndex (but implemented differently) or mapWithIndex, because they don't do lazy data structures by default. (And in eg Python their lazy sequences come with lots of caveats because they are not referentially transparent.)
- hellofunk 7y agoWhy not just pass the reference instead of an iterator or index? That’s what I do the best majority of the time. It’s rare that I actually need a specific iterator or pointer. Just change your loop to auto& x.
- 7y ago