Live data from Hacker News

Why Isn't Functional Programming the Norm? [video]

youtube.com

381–390 of 417 posts

Re: Why Isn't Functional Programming the Norm? [video]

#381
post #142
post #30

The top comment on YouTube raises a valid point: > I've programmed both functional and non-functional (not necessarily OO) programming languages for ~2 decades now. This misses the point. Even if functional programming helps you reason about ADTs and data flow, monads, etc, it has the opposite effect for helping you reason about what the machine is doing. You have no control over execution, memory layout, garbage col…

> FP will always occupy a niche because of where it sits in the abstraction hierarchy At some point in history, people stopped worrying about not understanding compilers, how they allocate registers and handle loops and do low-level optimizations. The compilers (and languages like C or C++) became good enough (or even better than humans in many cases) in optimizing code. The same happened with managed memory and data…

99% of the time you can forget about them, but when you get performance problems then you need to start digging in to what goes on behind the scenes.

Re: Why Isn't Functional Programming the Norm? [video]

#382
post #195

OOP was designed to prioritize encapsulation at the expense of referential transparency. Functional programming was designed to prioritize referential transparency at the expense of encapsulation. You cannot have referential transparency and encapsulation at the same time. In order to prevent mutations (which is a requirement of FP), a module cannot hold any state internally; this necessarily means that the state mus…

" the more interchangeable the components will be. It's like Lego blocks; " This is precisely the reason why pure FP is prioritizing referential transparency. Even if objects are perfectly encapsulated, with enough complexity, because other objects will depend on that information, and because that information mutates and changes over time, this is bound to cause some errors. Compilers can't check program correctness…

>> Even if objects are perfectly encapsulated, with enough complexity, because other objects will depend on that information, ...

I would argue that when other objects from different parts of the code depend on the same state and there is no clear hierarchy or data flow direction between those objects, then that is going to cause problems regardless of whether the language is OOP or FP. The problems will manifest themselves in different ways but it will be messy and difficult to debug in either case (FP or OOP) because this is an architectural problem and not a programming problem. It will require a refactoring.

OOP helps to reduce architectural problems like this because it encourages developers to break logic up into modules which have distinct, non-overlapping concerns.

Re: Why Isn't Functional Programming the Norm? [video]

#383
These things are not all-or-nothing.

I have one compute device, the GPU, that I program with its language (eg GLSL, OpenCL).

I have another compute device, the CPU, that I program with its language (eg C, C++).

I have code to control these devices, that mostly handles scheduling and waiting on the results of these computations (as well as network traffic and user input), and I program that in a language that supports functional style (eg C#, TypeScript).

Re: Why Isn't Functional Programming the Norm? [video]

#384
post #356

Earlier quoted context omitted.

Never written even a simple C compiler, but I, and most c++ programmers that care about performance I think, do have a decent idea what code g++ is going to generate.

But g++ is probably better than you at producing fast code for whatever architecture you run it on.

Of course it is, that's why I let it generate it.

Re: Why Isn't Functional Programming the Norm? [video]

#385
post #279

Earlier quoted context omitted.

Personally, I get frustrated that there seems to be a belief that you can only use FP or OOP, when the reality is both models can be used in conjunction, and there may be reasons to choose one over the other dependent on what you are doing. Not to mention there are other models such as Protocol Oriented Programming. You see this in languages like Swift.

The issues is that you get benefits for sticking to one paradigm, because then everything is made of the same stuff. If everything is an object, then you can use all your tooling that works with objects, which is everything. If everything is pure, you get easy parallelism. If everything is an actor, you get easy distributability. If everything is a monad or function, you get easy compositionality. The list goes on. S…

You don't really get easy parallelism, do you? People used to theorise this, but auto-parallelisation never really worked because it's hard to understand - even for a machine - where the cost/benefit tradeoff of parallelism starts to happen. Applying a pure function to a massive list is simply not a common pattern outside of things like multimedia codecs or 3D graphics, where these industries settled on explicitly expressing the data parallelism using SIMD or shaders a long time ago. Functional languages have nothing to add there.

Re: Why Isn't Functional Programming the Norm? [video]

#386

Earlier quoted context omitted.

Yes, and this is one of the areas where functional programming really shines. An imperative program is defined as a series of ordered steps and the compiler can't (in general) reorder steps to optimize use of resources because the steps could have arbitrary side-effects.[1] The FP version is essentially a dependency graph which constrains the order of operations without mandating a specific final order. The pre-heate…

> the compiler can't (in general) reorder steps to optimize use of resources i'm not sure what you mean by that because compilers reorder instructions to improve performance all the time (and CPUs do it dynamically too).

Compilers and CPUs only reorder over tiny instruction windows. He's talking about re-orderings over enormous windows, in a way that requires whole program analysis.

But that doesn't really happen in reality. FP languages promised auto-parallelisation for decades and never delivered. Plus you can get it in imperative languages too - like with Java's parallel streams. But I never see a parallel stream in real use.

Re: Why Isn't Functional Programming the Norm? [video]

#387

Earlier quoted context omitted.

Yes, and this is one of the areas where functional programming really shines. An imperative program is defined as a series of ordered steps and the compiler can't (in general) reorder steps to optimize use of resources because the steps could have arbitrary side-effects.[1] The FP version is essentially a dependency graph which constrains the order of operations without mandating a specific final order. The pre-heate…

> the compiler can't (in general) reorder steps to optimize use of resources i'm not sure what you mean by that because compilers reorder instructions to improve performance all the time (and CPUs do it dynamically too).

I mean that an imperative program spells out a particular order of operations and the compiler is forced to reverse-engineer the dependencies based on its (usually incomplete) knowledge of each step's side effects. When the potential side effects are unknown, such as for calls to shared library functions, system calls, or access to shared global data, or any call to a function outside the current compilation unit in the absence of link-time optimization, then it must preserve the original order even if that order is less than optimal.

The kind of reordering you see in imperative programs tends to be on the small scale, affecting only nearby primitive operations within a single thread. You don't generally see imperative compilers automatically farming out large sections of the program onto separate threads to be evaluated in parallel. That is something that only really becomes practical when you can be sure that the evaluation of one part won't affect any other part, i.e. in a language with referential transparency.

Re: Why Isn't Functional Programming the Norm? [video]

#388
post #67
post #5

Earlier quoted context omitted.

You may want to consider Ramda.js instead: https://ramdajs.com/ IMHO does a better job than Lodash, because: 1. All functions are automatically curried. 2. The order of parameters lends itself to composition. EDIT: 3. Transducers.

I never understood the point of Ramda. It's like it's trying to replace the core functionality of JS with something that's completely orthogonal to what the language actually is, but it's just a bolted on library. I've worked on codebases where people ignore all built-in JS functions (like Array.map/filter) and write Ramda spaghetti instead with multiple nested pipes and lens and what not to show off their FP purism.…

You stick with your “polyfill”, I’ll stick with mine , so to speak.

It’s nice to be able to make a function as concisely as something like:

Const foo_finder = R.find( R.propEq( ‘prop’, ‘foo’ ) )

...

Const a_foo = foo_finder( a_list )

Re: Why Isn't Functional Programming the Norm? [video]

#389
post #362

Earlier quoted context omitted.

Yes they are documented, though the documentation is really something like "-fawesome-optimization, enabled by default on -O3" and the "-fawesome-optimization" has documentation like "enables awesome optimization" without explaining much more than that. And even then pretty much every project out there uses "-Ofast" instead of whatever "-Ofast" enables without caring about what it does or how its behavior will change…

-Ofast enables fast-math optimizations and generally is not standard compliant. I hope projects do not deliberately enable it without thinking (as they say, it is hard to make stuff fool proof because fools are so resourceful).

My point was that options like -O and -Ofast aren't the actual optimization switches, they turn on other switches and you do not know what you'll get - essentially wishing for fast code and hoping you'll get some (i mentioned -Ofast explicitly because of its name).

For example according to the documentation in GCC 7.4 -O3 turns on:

    -fgcse-after-reload
    -finline-functions
    -fipa-cp-clone
    -fpeel-loops
    -fpredictive-commoning
    -fsplit-paths
    -ftree-loop-distribute-patterns
    -ftree-loop-vectorize
    -ftree-partial-pre
    -ftree-slp-vectorize
    -funswitch-loops
    -fvect-cost-model
whereas in GCC 9.2 -O3 turns the above, plus:

    -floop-interchange 
    -floop-unroll-and-jam 
    -ftree-loop-distribution 
    -fversion-loops-for-strides
So unless you control the exact version of the compiler that will generate the binaries you will give out, you do not exactly know what specifying "-O3" will do.

Moreover even though you do know the switches, their documentation is basically nothing. For a random example what "-floop-unroll-and-jam" does? The GCC 9.2 documentation combines it with "-ftree-loop-linear", "-floop-interchange", "-floop-strip-mine" and "-floop-block" and all it says is:

> Perform loop nest optimizations. Same as -floop-nest-optimize. To use this code transformation, GCC has to be configured with --with-isl to enable the Graphite loop transformation infrastructure.

...what does that even mean? What sort of effect will those transformations have on the code? Why are they all jumbled in one explanation? Are they exactly the same? Why does it say that they are the same "-floop-nest-optimize"? Which option is the same? All of them? The -"floop-nest-optimize" documentation says:

> Enable the isl based loop nest optimizer. This is a generic loop nest optimizer based on the Pluto optimization algorithms. It calculates a loop structure optimized for data-locality and parallelism. This option is experimental.

Based on the Pluto optimization algorithms? Even assuming that this refers to "PLUTO - An automatic parallelizer and locality optimizer for affine loop nests" (this is a guess, no other references in the GCC documentation as far as i can tell), does it mean they are the same as the the code in pluto, that they based on the code and are modified or that they are based on the general idea/concepts/algorithms?

--

So it isn't really a surprise that most people simple throw out "-Ofast" (or -O3 or -O2 or whatever) and hope for the best. They do not know better and they cannot know better since their compiler doesn't provide them any further information. And this is where all the FUD and fear about C's undefined behavior comes - people not knowing what exactly happens because they are not even told.

Re: Why Isn't Functional Programming the Norm? [video]

#390

Earlier quoted context omitted.

> the compiler can't (in general) reorder steps to optimize use of resources i'm not sure what you mean by that because compilers reorder instructions to improve performance all the time (and CPUs do it dynamically too).

Compilers and CPUs only reorder over tiny instruction windows. He's talking about re-orderings over enormous windows, in a way that requires whole program analysis. But that doesn't really happen in reality. FP languages promised auto-parallelisation for decades and never delivered. Plus you can get it in imperative languages too - like with Java's parallel streams. But I never see a parallel stream in real use.

It's not completely automatic but it is fairly close. If you enable the threaded runtime then "sparks" will be evaluated in parallel. You do have to say which expressions you want evaluated as separate "sparks" with the `par` operator but that's it—the runtime manages the threads, and common sub-expressions shared by multiple sparks will typically be evaluated only once. There are no race conditions or other typically concurrency issues to worry about since the language guarantees the absence of side effects. (That is the biggest difference between this and Java's parallel streams: If the reduction operation isn't a pure function then the result is undefined, and there isn't anything at the language level in Java to enforce this requirement.)

EDIT: An example of effective parallelism in Haskell:

    import Control.Parallel (par)

    fib n
       | n = 15 = b `par` a `seq` a + b
       | True    = a + b
       where a = fib (n-2); b = fib (n-1)

    main = print $ map fib [0..39]
Note that the implementation of `fib` has been deliberately pessimized to simulate an expensive computation. The only difference from the non-parallel version is the use of `par` and `seq` to hint that the two operands should be evaluated in parallel when n >= 15. These hints cannot change the result, only the evaluation strategy. Compile and link with "-threaded -with-rtsopts=-N" and this will automatically take advantage of multiple cores. (1C=9.9s elapsed; 2C=5.4s; 3C=4s; 4C=3.5s)
Post reply on HN