25 ms·
A lot of pleas for functional programming give a long list of convincing examples that work well with functional programming. You can do this with almost any la
by kutkloon7 10y ago
A lot of pleas for functional programming give a long list of convincing examples that work well with functional programming. You can do this with almost any language, and this is also quite deceptional, as this is often done to convince beginners to learn another language; "look at how simple it is to program this contrived example in <language X>!".
In order to be able to use a programming language or paradigm well, it is especially useful to know what its weaknesses are. Functional programming does not work well in programs which handle a lot of states. It does not work well in programs which have high requirements for performance or memory. It does not work well for programs which have to do low-level stuff.
These are serious disadvantages, and I would like to see them highlighted more often in this introductory-style articles. Nevertheless, I think functional programming is a must to be able to write simpler programs, and I think programmers should write functions (i.e. methods that only depend on their arguments) whenever possible, for modularity and correctness reasons. I think the disadvantages can be solved by non-pure functional languages, and that there is a lot to gain for new programming languages in this area.
- amirmc 10y ago> I think the disadvantages can be solved by non-pure functional languages, and that there is a lot to gain for new programming languages in this area. We don't necessarily need new languages for this. There are already pragmatic languages out there, for example, OCaml [1] (and if the syntax is off-putting there's ReasonML [2] from Facebook). I'm sure there are other examples too. [1] http://ocaml.org http://ocaml.org [2] http://facebook.github.io/reason/ http://facebook.github.io/reason/
- shultays 10y agoHere is cat in brainfuck ,[.,] Why it is not year of the brainfuck programming is beyond me
- dualogy 10y ago> Functional programming does not work well in (cont..) You state earlier that programmers can and perhaps should write functional style (AFA feasible) in the languages that do excel at above problems. So you probably meant that "functional languages" ---rather than "functional programming"--- "does not work well" there. > (..cont) programs which handle a lot of states. It does not work well in programs which have high requirements for performance or memory. It does not work well for programs which have to do low-level stuff. The main problem with these issues is that there's no serious investment into optimizing the very few pure-and-lazy (for me the holy grail of FP) languages' (most-all of academic origin) compilers better for real-world loads such as you outlined. The described problem spaces themselves ("lots of states" -- huh?) are surprisingly simple to model in the type systems of the languages I'm into such as Haskell. Side effects are most elegantly solved and defining them yields succinct and non-ambiguous code. And really just like OOPs design classes to describe their problem spaces and stuff their logic into, so you start from your types in FP. That being said, so far I'm rather satisfied with GHC speeds and resulting binary performance. That is, of course, for non mission-critical-realtime-high-frequency-trades-while-raytracing-while-guiding-missiles use-cases only so far. A lot of automation I do, I really couldn't give a hoot about its performance as long as I'm not billed by the second (which you shouldn't be anyway). Because, hey it's automation! It means I can do other stuff! Likewise I don't care whether a dishwasher takes 1 or 3 hours. Adjust expectations and use the time freed: slower automation yields more time freed! Wait..
- mikhailfranco 10y agoThe dishwasher case comes down to: period between meals; and capex/storage for duplicating crockery and kitchen utensils. So I would say there is a threshold around 3 hours, 1 hour good, 5 hours bad. It is also a factor of volume, so 1 person unnecessary, 4 person family (or whatever they call it these days), probably essential.
- jlouis 10y agoFunctional programming largely has two schools: Treat "commands" as a separate entity from expressions, and bake "commands" into expressions. The former is largely Haskell, Clean, ... and the latter is exemplified by e.g., Standard ML or OCaml. There are trade-offs between the two, but I definitely belong to the second school: we simply add an imperative subset to our functional language. This means we can exploit any efficiency trick an imperative program can do, including low-level stuff. The seasoned FP programmer will then proceed to encapsulate the efficiency trick in a abstract module such that the rest of the program doesn't have to worry about it. Of course, the price to pay for this is that you are losing purity. I think this is a fair trade-off but others disagree. As for performance and memory usage: it is always a property of the architecture or system, not of the programming language. Dropping to a low-level language, such as C, usually doesn't buy you too much these days. What is more important is that most C compilers in use have vastly more time invested into optimizing routines than the typical FP compiler. Apart from that, you can easily mange the same kind of data in e.g., OCaml than you can in C. The reason FP can beat the curve of performance in practice is because you operate at a higher level of abstraction. You have more attempts at writing the correct architecture, and it is easier to change over time. Since most real-world problems are heavily time-constrained, this makes them beat low-level solutions: when the C programmer has written the first working version, the FP programmer has tried 5 different solutions. There is one area FP tends to fare poorly: CPU bound tasks where an inner-loop has to squeeze out performance (video encoding comes to mind). But most low-level programming fares poorly as well: either you use assembly, write GPU-level programs, use an FPGA or create your own ASIC/SoC solution for this. Also note that moving to faster solutions here costs an order of magnitude in time and in dollars: FPGAs are, relatively speaking, expensive beasts.
- pka 10y ago> This means we can exploit any efficiency trick an imperative program can do, including low-level stuff. The seasoned FP programmer will then proceed to encapsulate the efficiency trick in a abstract module such that the rest of the program doesn't have to worry about it. You can do that in a pure language as well (i.e. ST monad.) Purity shouldn't be given up lightly.
- ahallock 10y ago> Functional programming does not work well in programs which handle a lot of states From what I understand, isn't this what functional programming excels at?
- nilved 10y agoYes. You can model state easily in functional programming languages but the technique is unfamiliar to imperative programmers since it's more explicit. This is a very good thing.
- darksaints 10y agoMore explicit? Try more convoluted. Ever try to build an event-driven FSM in Haskell? I've seen several try, including myself...the results of which have all been complexity disasters. Imperative languages in general have poor data structures for representing state (no ADTs!), but nothing is more explicit and straightforward than using mutable data or mutable containers for modeling actual mutable state. That's why languages like Scala exist. Functional without religious paradigm enforcement.
- nickpsecurity 10y agoI don't do FP or Haskell but did do FSM's in imperative languages. Looking up Haskell's event-driven FSM's... https://wiki.haskell.org/Real_World_Applications/Event_Driven_Applications https://wiki.haskell.org/Real_World_Applications/Event_Drive... https://accu.org/index.php/journals/2199 https://accu.org/index.php/journals/2199 ...gives me code that looks weird since I did imperative but straight-forward. The second link even shows some simplicity and less tangling vs imperative examples. I wonder what makes you think FSM's in Haskell turn into disasters if these examples were so easy. Besides, you shouldn't be hand-coding FSM's to begin with. The various sub-fields of programming and IT I've studied all came from different directions to same best practice for FSM's: DSL's that generate them. LISP people did it forever. iMatix did it for reliable, distributed apps in C. Haskell embeddings go from stuff like that up to hard real-time C. Theres also open-source projects that compile easy descriptions of them to about any language. The DSL can be included in repo right next to resultant code for documentation purposes. Hardware designers also synthesize and transform them with automated tools. Given that, the difficulty of doing FSM's by hand should never be a problem for any language. Just don't do it by hand. Automate the tedious stuff machines are good at. Hand-code the stuff humans are good at.
- Lewton 10y agoThat's one of the reasons I loved the "programming languages" course on coursera[0]. The course went through 3 languages, ML, Racket and Ruby giving 2 weeks to each language. And then spent time contrasting the weaknesses and strengths of the different languages and their paradigms. [0]https://www.coursera.org/learn/programming-languages https://www.coursera.org/learn/programming-languages (Starts up again on the 9th of January :) )
- the_af 10y agoSeconded! Programming Languages is wonderful. Easily the best I've taken in Coursera (and better than Odersky's courses).
- bogomipz 10y ago>"It does not work well in programs which have high requirements for performance or memory" Can you elaborate on why this is? I see the parent post mentioned "lazy evaluation." Can you say why this is relevant to discussions on resource utilization and performance?
- kqr 10y agoHigher levels of abstraction are harder to translate to efficeint machine code. Non-strict (what you call "lazy") evaluation can make it more difficult to predict when resources are needed. In strict languages, the resources needed to evaluate f() are needed exactly at the point where you typed "f()". With non-strict evaluation, those resources may be needed then, later, or not at all! If those resources happen to be needed when a) they are no longer available, or b) at the same time as a bunch of other computations need resources, you have problems.
- bogomipz 10y agoThat makes total sense. Thanks for the explanation.
- century19 10y ago-- Functional programming does not work well in programs which handle a lot of states Apache Spark is written in Scala. It can handle extremely large amounts of state across large clusters of machines. Is the argument here that Scala is not purely functional? Or what am I missing?
- paulddraper 10y agoBy "lots of state" I don't think he meant "lots of data", but rather data that get changed a lot: Q&A forum, CRUD application, etc. Functional paradigms can still be used (e.g. HackerNews, though let's be honest...I love it but it's not the most challenging feature set). But FP isn't as natural of a fit as the problems chosen to introduce it.
- century19 10y agoNot saying your wrong about what he meant, but Spark can also keep state across the Cluster, not just data. Check out the "UpdateStateByKey Operation" in the following page. http://spark.apache.org/docs/latest/streaming-programming-guide.html http://spark.apache.org/docs/latest/streaming-programming-gu...
- teirce 10y agoI haven't taken a look at the Spark codebase, but it is very easy to write Scala code that is not purely functional.
- SatvikBeri 10y agoI write code for a living (data science), but am not really a programmer. So far, my favorite language is Scala with Spark. Other things I've worked with: Python, Clojure, Haskell, R, SAS, tons and tons of SQL. The major advantage of functional programming here is that it models the domain really well: the vast majority of my work involves taking a small number of data sets, doing a long sequence of processing, and outputting a small number of data sets. Everything in between benefits substantially from error-preventing techniques such as purity and static types. The biggest disadvantage is how hard it is to analyze and improve performance. Working with Spark means that a pipeline that works on X gb of data will often fail on 5X with out of memory errors, and it's nontrivial to diagnose and fix. It's not clear to me how much of this is due to Spark itself vs. laziness.
- KirinDave 10y ago> In order to be able to use a programming language or paradigm well, it is especially useful to know what its weaknesses are. Functional programming does not work well in programs which handle a lot of states. It does not work well in programs which have high requirements for performance or memory. It does not work well for programs which have to do low-level stuff. By the same token, whenever this conversation comes up a series of vague and poorly-formed criticisms come up saying, "You can't do low level things" without defining what on earth that means. Or, perhaps worse, conflating the idea that you must have Haskell in all its massive (and perhaps fairly: a bit bloated) confusion of ideas. People can and do write high performance code in functional style and with functional tools. People even do it with laziness as a core abstraction. The main gate to functional languages participating in, say, the Linux kernel is NOT that they are "too slow" or that "laziness makes them too confusing". It's that the Linux kernel is written entirely around the unique weirdness and expectations of C, and only languages based on or descendant to C do well there. It's difficult to treat the core question, "What are the disadvantages of functional programming" in the same way that it's difficult to answer, "What are the weaknesses of OO programming." Both use terms that encompass a very wide variety of approaches and decisions, but the way we evaluate them as bottom-up, on a case-by case language for each individual language.
- johncolanduoni 10y ago> The main gate to functional languages participating in, say, the Linux kernel is NOT that they are "too slow" or that "laziness makes them too confusing". It's that the Linux kernel is written entirely around the unique weirdness and expectations of C, and only languages based on or descendant to C do well there. Sure, but the usual functional style has intrinsic issues that prevent it from being feasible for writing kernels in general. A kernel (especially a microkernel) spends most of its time managing state. You can use a functional language as a metalanguage for an imperative DSL (as the Atom DSL for constant-space programming uses Haskell), but you won't be writing code that looks remotely functional.
- KirinDave 10y ago> Sure, but the usual functional style has intrinsic issues that prevent it from being feasible for writing kernels in general. Not really? > A kernel (especially a microkernel) spends most of its time managing state. So does every computer program though. The idea that functional languages can't support mutation is a strangely persistent myth even in the face of multiple counter-examples AND 20 years of improvement via research and practical work. > but you won't be writing code that looks remotely functional. Many useful & powerful functional abstractions can be written to use constant space, even in Haskell.
- wyager 10y ago> Functional programming does not work well in programs which handle a lot of states. It does not work well in programs which have high requirements for performance or memory. It does not work well for programs which have to do low-level stuff. None of this is true. This is just the generic set of plausible-sounding but meaningless complaints people who haven't actually used FP for any of these purposes tend to repeat. > a lot of states What is this supposed to mean? ADTs are by far the best tool for state management available in programming today, and yet there are very few non-functional languages that support them. Languages like Haskell offer extremely powerful tools for state management like monad transformers and ADT-based exception management. > high requirements for performance or memory This is an old meme that doesn't apply at all anymore. Haskell has a number of world-class packed data management tools (repa, vector, bytestring, etc.) that can actually do a ton of cool optimizations that a similar library in e.g. C++ could not do. Any marginal overhead incurred by using a functional style (which you are not obligated to use) are typically more than offset by the fact that high-efficiency techniques that are typically inconvenient to represent (like cache-sized chunked text management) are extremely easy to use with strong enough types and flexible enough combinators. For example, if you're doing streaming text management, you can get higher performance in C than Haskell, but it's going to take 100x more effort over just using lazy ByteStrings. Tight numerical code will get unpacked and turned into more or less C-equivalent assembly. > low-level stuff. I'm not sure what your definition of "low-level" is. For me it's doing register operations on microcontrollers, and in that case I agree. But for anything you can do on Linux, you are incorrect. Haskell has better low-level support via FFI mechanisms than, say, Java.
- willtim 10y agoI don't believe many are advocating functional programming as a replacement for hand-crafted C, but it is a good alterative to imperative high-level languages which often make big performance sacrifices but offer only small improvements in reliability and abstraction/composition. I absolutely believe the correct way to build software is with language layers. Ideally using FP or declarative languages where you can. Python gets this partially right by using C libraries for all its performance critical work. C++ is IMHO the wrong approach, a jack of all trades and a master of none.
- pjmlp 10y agoWhy not? Lisp did it before C was even born.
- willtim 10y agoHow many device drivers have ever been written in Lisp? I'm not necessarily advocating C as the best way to write high performance, non-allocating code either.
- pjmlp 10y agoEver heard of Interlisp-D, Lisp Machines?
- lispm 10y agoThe problem: this was long ago on an OS written largely in Lisp. On a current typical OS (Windows, Android, iOS, macOS, Linux, ...) the chance of ever seeing a Lisp-based device driver in action is near zero.
- pjmlp 10y agoIt isn't because those ideas failed to get mass adoption that we should ignore them.
- runT1ME 10y ago>Functional programming does not work well in programs which handle a lot of states. I'd say this is completely false, and almost no one agrees with this if they've done any actual functional programming.
- emerongi 10y agoI'd say that state is actually easier to handle in functional languages. You just need the right abstractions for it. I've been working with Phoenix lately, which basically just hands request/response details down a pipe of functions, which all can change the state of the response. There can be a lot of state contained in this response, but when you use Phoenix, you don't really notice it. The abstraction makes everything seamless - best web-framework I've used to date.
- greggman 10y agoI'm too much of a noob to know what I'm talking about but so far this series of talks has convinced me FP with strong typing brings something new to the table in the same way that say C brings something over assembly and s expressions in lisp bring something over non s expression languages. http://fsharpforfunandprofit.com/ http://fsharpforfunandprofit.com/ In particular this example convinced me I'm missing out on something http://fsharpforfunandprofit.com/posts/designing-for-correctness/ http://fsharpforfunandprofit.com/posts/designing-for-correct...
- kazinator 10y agoWell, C brings ... functional programming over assembly, right? Look, calculation with machine words, but without side effects: int x = 42; int y = x / 2; double z = sqrt(x*x + y*y); return 1 + z; We're just binding fresh variables, and nesting expressions that evaluate terms and return results. No assignment anywhere. Yes, functional programming matters. It lets you add two things together in C without worrying about allocating a destination operand for the result, whose clobbering won't affect anything anywhere else. This sort of thing in turn makes it a heck of a lot easier to write OS schedulers, drivers, memory managers, codecs, ray tracers, database engines, ...
- whateveracct 10y agoNOTE: I agree with your comment as a whole but I want to give some counterpoints to some things you've said. These counterpoints all do require more "advanced" FP ability, but as FP becomes more popular, these techniques will become less "advanced." > Functional programming does not work well in programs which handle a lot of states. I've rewritten stateful, greedy, backtracking algorithms from Java to Scala. Scala (scalaz in particular) made this algorithms much simpler with no noticeable performance penalty. The backtracking in particular became trivial thanks to immutability and the State monad. > It does not work well in programs which have high requirements for performance or memory. There is a cost to immutability, but mostly it doesn't matter. When it does, FP has a lot of ability to encapsulate mutability/ugliness etc used for performance improved in different ways. Haskell in particular is great at this. It has ST for mutability, and rewrite rules/inline pragmas for performance gains. There still is a lot of room to improve here however. > It does not work well for programs which have to do low-level stuff. Haskell itself isn't ideal for low-level programming (low-level as in microcontrollers and FPGAs. Raspberry Pi isn't low-level and can handle Haskell fine). But Haskell has EDSLs that compile to C/VHDL/etc (C: Ivory, VHDL: Various Lava variants). With an EDSL approach, you end up using Haskell as a macro language for your low-level language. And unless the CPP, Haskell is a powerful and easy to reason about macro language!
- marxama 10y agoWith regards to performance, sometimes taking the penalty of immutability in the small leads to gains in the large. The React wrappers in ClojureScript are a great example of this - since all data is immutable, shouldComponentUpdate is trivially and automatically implemented for you, meaning React not having to diff parts of the virtual DOM that are guaranteed to be identical. There are other cases where immutability helps, even with regards of performance. At my day job, we use C#, with little focus on immutability. Performance has been a huge problem lately. One of the things we did was to change lots of constructors which initialized empty lists. In most cases, these lists remain empty, so we created a whole bunch of objects unnecessarily, which put a lot of toll on the GC. The "solution" was to initialize them to null instead, which made the code a lot more brittle and cumbersome. Had we used immutable lists instead, we could've shared one single, empty object instead (per type, of course). We also have lots of copy constructors which we use often - these would be completely unnecessary with immutable data.
- pmarreck 10y agoThe longer time I spent programming, the more at least the functional paradigm (even within non-functional languages) made sense, mostly as a labor-saving device (like all things in programming) resulting in fewer bugs, easier modularity, easier unit testability, etc. etc. Apparently I'm not alone, here's John Carmack's take on it after "trying it on" for a while in C++, he has some very strongly positive things to say about it: http://www.gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php http://www.gamasutra.com/view/news/169296/Indepth_Functional... You haven't listed the specific disadvantages of FP, one I noticed with a bit of dismay was that the code for a basic quicksort in functional languages looks like this (Elixir in this case: https://gist.github.com/rbishop/c742ab53b12efc162176 https://gist.github.com/rbishop/c742ab53b12efc162176) and is actually quite beautiful BUT performs fairly horribly. (It is rare for me to find code that looks this good/simple and yet is one of the worst-performing.) The same algorithm is also slow in Haskell (which is where I first saw it). Some might cite the preponderance of recursion instead of looping as a fault or flaw (since the language implementation needs to then implement TCO in order to not trivially blow the stack... and the programmer needs to be AWARE of how to trigger TCO), but in practice I prefer it because a semantically-infinite-recursing process ends up being a "nicer" paradigm even if in actuality it's implemented underneath with a loop construct.
- deleted 10y ago[deleted]