3 ms·
I'm concerned about the use of functional languages. I like them but they seem to be treated as an implementation, not a model, and as such can be very ineffici
by tempguy9999 8y ago
I'm concerned about the use of functional languages. I like them but they seem to be treated as an implementation, not a model, and as such can be very inefficient. I've used scala and like it, but basic stuff with flatmap etc can unfold to be a gross mess of code which, had I written it non-functionally, would be vastly more efficient.
I'd expect the compiler to do more work, perhaps with some additional hints from me, go get efficiency. SQL does this because it has to (not with total success) but with scala I have had to abandon 'nice' functional stuff, which I like, to get speed. It's disappointing. I shouldn't be doing the compiler's job. With a literal implementation, functional programming just seems inevitably expensive and that's morally wrong (just cos computers are fast doesn't mean cycles are free, and don't quote the programmer time vs computer time stuff at me, wasting hardware can become more expensive as the number of machines increases).
Optimising FP to the extent I want is something I'm sure the compiler can't do alone; it would require hints and pointers from the human. The necessary optimisations would need to be from high-level functional transformations (eg. map f (map g x) -> map f.g x), down to eg. escape analysis so as to not produce so much garbage, down to data layout to stop the thrashing the cache.
Also someone I worked with just went quite mad on it - FP should be used where it fits best, he forced us to use it exclusively, even when a procedural code would simply have been simpler and clearer. And faster too - the end result just crawled (it was scala btw). Not a fault of the paradigm but of overselling, and someone in dire need of common sense.
Multiple disclaimers: I'm not a scala expert and haven't used it for a couple of years. I'm not an FP expert. What I've said above is a brief overview of a much larger and more nuanced criticism, which I don't have time to give; I hope you see what I'm getting at. I have little experience with other FP languages so what I say is based on scala and maybe eg. haskell does the clever stuff.
Edit: tidied up.
- gizmo686 8y agoHaskell (or, rather GHC) does do clever stuff, and gets idiomatic code competitive performance with other languages [0]. The problem is that a lot of what GHC does require going full on functional in order to be practical. For instance, GHC can easily produce garbage of 1GB/s [1], which it can handle far easier than most languages because everything is immutable. GHC also does not have a stack [2]. You can get something resembling a traditional call stack, but it has a fair amount of overhead. > map f (map g x) -> map f.g x) This transformation only works with pure functions. I am currently using Scala, and it is (IMO) a very weird mix between FP and OOP. I is (IMO) much closer to Java style OOP than it is to Haskell style FP (not suprising considering its relationship with Java). It seems to inherit a lot of restrictions from Java that limit its ability to do the type of optimizations that are necessary for performant FP. If you actually look at the JVM call stack of a scala program, you will find an insane amount of indirection. More than even a naive implementation of FP in C (undoubtadly because of how it interacts with its object system and the JVM) [0] Although reasoning about performance is still pretty hard. [1] https://wiki.haskell.org/GHC/Memory_Management https://wiki.haskell.org/GHC/Memory_Management [2] Actually, it does have a stack, but the stack is no where near what you would expect.
- tempguy9999 8y ago> GHC [...] idiomatic code competitive performance with other languages I thought this only was on micro-benchmarks? > GHC can easily produce garbage of 1GB/s [1], which it can handle far easier than most languages because everything is immutable. How can you possibly not totally trash caches and destroy performance with that kind of churn? From your link "New data are allocated in 512kb 'nursery'. Once it's exhausted, 'minor GC' occurs". I don't know but that sounds like kissing your entire L1 data cache goodbye, and then some. (stuff about GHC stack) - Most curious! Got a link? (cactus stack is it?) > This transformation only works with pure functions. for sure, FP transformations tend to require that but it reminds me that scala doesn't have a this-function-is-pure declaration, which I'd really like. Along with deep-constantness. > It seems to inherit a lot of restrictions from Java that limit its ability to do the type of optimizations that are necessary for performant FP. Not so sure. Scala seems to do a very literal translation - there was a library that showed this, I can't find it, was on github, where straightforward but messy desugaring was replaced with simple stuff. OK, let me try this instead... invented syntax, can't remember how to do it in scala, sum of squares: SumSq lst = reduce (\x, y: x + y) map (\z: z * z) lst I'm pretty sure scala would compile that literally a few years ago and I wonder if it does any better now. I'd expect that, over a thousand item list, to call the square lambda a thousand times to construct an intermediate, then call the add function a thousand times to reduce it. The above should be compilable to: SumSq lst = var rslt = 0; for(i in lst) { rslt += i * i }; rslt; They're not quite equivalent as the first will fail on an empty list but you see what I mean. Point is, there's nothing in the JVM that prevents this level of optimisation. The second does mem reads only and can operate in a couple of registers, and can be unrolled and seems maximally efficient in every way I can think. If FP doesn't use the very optimisations that it enables, it's going to pay for that in lack of takeup. Inefficiency is not just a technical issue, I'd say it's even a moral one - wasting resources is immoral.