“Mostly functional” programming does not work
queue.acm.orgTo play devil's advocate for a minute:
Nearly all useful, reusable software today is written in a mostly or entirely imperative language. This is despite the fact that functional programming has been around for at least 20-30 years. So, my basic question is, if functional programming is so much better, why isn't more software written in a functional language? Or put another way, why are there so many blog posts promoting functional programming when it clearly hasn't produced results.
That's a good question and a proof that pure FP isn't good for creating "stuff that works". For years the mantra was "bad industry doesn't want to use FP". But nowadays, especially inside a community like HN, this statement doesn't make much sense. There are plenty of side projects and startups, and how many of the successfull ones use pure FP?
I think the reason for this is that pure FP replaces classic programming with puzzle solving. It satisfies math-oriented minds by forcing usage of this math-oriented layer. But it looks like software isn't math and you often need to work with a computer in a more direct way.
Functional programming has been around since the late 50s/early 60s, depending on what you count as the start of LISP as a programming language.
In some ways the reason it isn't used a lot is for historic reasons as much as anything else. Functional programming, for a long time, was seen (often rightly so) as more computationally expensive than the same imperative code for most tasks. For a long time, that meant that you weren't going to use functional programming: when every single byte and cycle matters, you're going to go with assembly or whatever is closest to it/translates really well to it. (e.x. C)
We now live in a world with abundant memory and excessive unused clock cycles. If I remember correctly, there's also been a good bit of research and work in the last 20-30 years The historic reasons for not using functional programming are gone for many (but definitely not all; people still hand roll assembly, too) of the original use cases. However, you then have 30+ years of programmers who were largely trained in and almost exclusively used imperative languages over the course of their career. That kind of market share is hard to compete with (because it's similar to market share; if everyone is using imperative languages, you need to be familiar with them too if you want to stay in most parts of the industry), and is self perpetuating (if you want to work in CS, you're going to need to know imperative languages for most jobs).
I'm also fairly certain there's a good number of useful, reuseable programs that're written in functional languages, but I'm in an airport and nothing immediately comes to mind.
Functional programming is something you learn, more than something you do. Every time you create a procedure without side effects, you're using the paradigm.
Learning when it's useful to use state, and restricting side effects to those parts of the program that require it, is a good skill to have. Now, pure functional languages may have had a slow adoption, but they are a good way to learn the paradigm that can be translated to other environments.
Also, if you think FP hasn't produced results you haven't been paying attention, or you don't program neither web applications nor database APIs. Declarative libraries like jQuery selectors and LINQ have spread like fire, and any programmer of complex asynchronous applications should take a look at Reactive Programming (which represent state changes in a functional style with the Streams and Futures abstractions) and Agent-based Models (which encapsulates them through message passing).
It does produce results, but the results are probably more hidden. Rumor has it that 50% or more of all telecommunication uses Erlang, a mostly-functional programming language. This is a tremendous success, but few people know of the fact.
I like to think that the whole discussion is straw-man. The useful, reusable software can be created in any language. But when every project is a gamble, and most projects starts out as imperative in orientation, then the chance of those bubbling to the top is greater. Good modularity of software happens on a higher level than picking the programming language of choice.
>It does produce results, but the results are probably more hidden. Rumor has it that 50% or more of all telecommunication uses Erlang, a mostly-functional programming language. This is a tremendous success, but few people know of the fact.
Probably the statement "> 50% of large telecommunications companies have done something with Erlang at some point in time" is correct. I guess you could even make it a 100% considering that parts of routers are often programmed with FP.
However, most stuff is done in imperative languages. ;) Routing and Connection handling is certainly nice using FP, just consider the amazing Hot Code Swapping features in Erlang. On the other hand, writing APIs, connecting those etc. is much more convenient in imperative languages.
After reading the article I tend to agree. Just this excerpt seems to confirm:
Pure functional programming is programming with mathematical functions. ... Calling a function with the same arguments will return the same result every time.
That leaves a whole lot of other functions, the non-mathematical ones, the real-life ones where you read files or I/O or user input out of the "Pure" definition, if I understand it correctly that is. And by that logic, means '"Pure functional" programming does work, but only if...'. Or, in other words: '"Pure functional" programming does NOT work in real-life software'
You can do a pure function that reads files or does IO. Heraclitus solved the algorithm millenia ago. You just need to include the right parameters.
Instead of
You just doreadFile(file_name)
If you change the value of complete_description_of_the_universe, then you've changed the input to your function, and of course you're going to get a different result back.readFile(file_name,complete_description_of_the_universe)Granted, a full complete_description_of_the_universe is rather space consuming. I don't even think it would fit on a floppy. Maybe a Zip drive?
Thankfully, you don't need the COMPLETE description. You only need the parts that your computer can access. There's no reason to store the amount of gas in the bowels of every elephant in India, unless you actually have an elephant gas sensor. So our function is really
Of course, that still means that the second parameter contains the contents of every bit on the hard drive, since the GMR sensor can measure those bits. Some lousy manufacturers are now selling computers that don't even have enough memory to hold their entire hard drive contents. Thankfully, we can use another optimization.readFile(file_name,complete_description_of_everything_the_computer_measure)Since, by definition, the computer can measure the value of complete_description_of_everything_the_computer_measure, there's no reason to store it in memory at all. We just measure it when it's needed. Granted, it's a little slow to read the hard drive, versus having the entire drive stored in memory, but premature optimization and all that.
It's still completely functional, though. We still pass the function two parameters. The first parameter is a set of bytes in memory with a EBCDIC representation of the name of the file. The second parameter is a physical computer that can perform the measurements. As long as you pass the exact same computer to the readFile function, you'll get the same result every time.
Same thing for writeFile. We pass in a physical computer and the function returns to us a new, physical computer that's similar to the one that we passed in, except that this computer has some different magnetic spin arrangements on its hard drive and its system clock is reading a different value.
Granted, carrying all of these physical computers around gets tiring, plus people start giving you guff about conservation of mass or whatever whenever you make a new computer. Thankfully, the functional compiler guys have found ways to optimize the code so that you can destroy the unneeded computers very rapidly. In fact, it's faster than the human eye can detect. Without careful measurement, you'd think that it was just one computer the whole time.
In practice, you don't actually need to return a new universe at all, because of course, that would be quite costly. Instead, the runtime can modify the existing universe and return it with a new unique identity - the reason we can do IO and remain referentially transparent is because every time we perform a side-effect, we do so on a unique universe - there is no way to duplicate a universe.
Haskell actually models the real universe better than any other languages - in the real world, we have the concept of now - some state of the current universe until an event elapses, when we have a new now, and we have a concept of the past, but in fact, this past does not exist anywhere - it was transient - we can't go back and modify this old universe to cause a change in the future, unless we introduce theories of parallel universes. the only thing that ever exists is the now, as you might learn from a Buddhist.
A Haskell function which does IO is just an event which causes a change from an old now in the universe to a current now, and the old one, the past, no longer exists.
I believe the whole fuss of monads is because they allow you to write pure functional programming while still doing I/O and similar things.
http://chris-taylor.github.io/blog/2013/02/09/io-is-not-a-si...
Ok, but if we're being picky about things, you can use all the monads you want, but if you read a file and display its output two different times, you may well get completely different results. The contents could have changed, someone may have removed the file, and so on.
Mathematically, you have two different inputs in that case, the environment changed since.
This logic oversimplifies: you are making the assumption that a function that talks to the outside world, such as 'read', is the same function at every invocation. You can deal with outside events (viz. IO) by using and returning a different 'read' function every time. Similarly, you can simulate state by feeding back a state object as an argument to the next invocation.
In practice functional languages have ways to mark certain code as having side effects for I/O and the like, which stops the badness from escaping to the rest of the program.
You're right in thinking that software with no side effects at all wouldn't be very useful.
I have a suspicion that it's due to the "unreasonable effectiveness of software", to paraphrase Eugene Wigner. The ability to do large amounts of computation quickly and cheaply is just so powerful that we can get away with doing it relatively badly and still create a lot of value.
Functional programming might well produce better software, for some criteria of "better", but these quality-based criteria are dominated by the "is it cheaper and more effective than paying a person to do this" criterion employed in industry. This isn't necessarily a bad thing - if even the "worst" imperative software is value-creating, then there's a lot of scope for superior functional software to create even more value, there just isn't a lot of pressure to do this within industry because imperative programs are good enough for most use cases.
There are other factors, such as path-dependency issues relating to developer skill-sets and toolchains, which give an advantage to imperative programming too. It seems likely to me that functional languages will close at least part of this gap, and perhaps Erik Meijer is underestimating the importance of "mostly functional" programming as a stepping-stone to "totally functional". Dijkstra was wrong when he claimed that learning BASIC was a mental mutilation preventing a person from ever understanding programming, and by the same token I suspect that we'll see plenty of people becoming good functional programmers who first encountered FP in passing callback handlers around in JavaScript.
This article is about pure functional programming.
As for functional programming, all of the recent languages (Rust, D, Go, Clojure, Scala, etc.) have functional features such as lambda functions. I don't think it's a stretch to say lack of support for functional programming is not an option for new languages.
As a couple of examples of software written in a functional language, consider Twitter and (ironically) Hacker News.
It is called legacy.
For several reasons, the hardware required to run Lisp code was too expensive vs what was required to run C, Pascal, Algol and their descendants.
Business don't change programming languages, just because. Unless you are doing greefield projects, there is a whole eco-system from tooling, building, trainings, developers, legacy code that needs to be taken into consideration.
By the time computers were getting cheap enough to handle FP at reasonable price, the OOP finally managed to get into the industry at the enterprise level. As such it got the place that could have been taken by FP.
OOP goes back to Simula and Smalltalk, which happen to also be Lisp inspired.
As for not producing results, many train schedule systems run on Lisp.
That's a red herring: You're asking why the majority of software is written in imperative languages instead of functional ones, which is a very complex question with very many factors. It is not an argument which invalidates nor counters the author's claim: Namely that "mostly functional languages" is unfeasible and only increases the bugs caused by side effects.
I get aware about functional language years after I learn to program. I think I lot of devs I know barely know it (in fact, because I'm dreaming in build a language, I get more info about).
Probably swift will be a significant cause of exposure for it.
However, I don't think a total functional language is the best, but a mix (like swift, julia).
> why isn't more software written in a functional language?
Because (1) honesty the “industry” programming is in the stagnation. Otherwise the boring Java♯'d run on Martin-Löf type theory; (2) it may require more brain; (3) we all learn imperative programming first.
(And of course this reasoning is ignoring one aspect: we (mostly) don't want to write bug-free software. Who would pay for support?)
By the same token, Windows is much better than Unix, McDonald's much better than a healthy diet, Justin Bieber is much better than [insert musician], ....
The author needs to be less aggressive in the accusations. This current trend which supposedly "doesn't work" actually runs the vast majority of the modern web consistently and reliably.
Placing onerous restrictions on what someone is permitted to do in order to satisfy some formal abstract programming model - that is the thing that really doesn't work too well.
This makes arbitrary programming arbitrarily difficult: many simple concepts are completely prohibited. Fanciful convoluted ways that don't violate the formalism have to be fabricated...because we can't violate our arbitrary formalism! No! Not in the name of instructing a computer to do something. /me adjusts his english headmaster cap.
You need to better learn what you are talking about. Passing data by pointer is perfectly allowable in functional programming, how do you imagine passing something by pointer has side effects?
Data.ByteString is basically a pointer to a byte array, and it's one of the most common datastructures in Haskell.
Even more importantly, the optimizations the Haskell VM is allowed to do because of the immutability and purity constraints means that even naieve and trivial solutions using Data.ByteString on general I/O problems like webservers perform in the best of class amongst native competitors like Nginx.
edit: And the majority of the web is ran consistently and reliably? You mean apart from it being threatened by huge security vulnerabilities almost every month for the past 20 years? With so much insecure systems still connected that DDOS attacks are a day to day concern for system administrators?
I didn't say Haskell. There are numerous programmings languages which only permit pass by value (and no referencing) for "I'm-such-a-smartypants" reasons.
I was really bashing the single-paradigm purist approach in general. It's much too rigid and difficult for anyone who isn't a card carrying mensa member.
Do you mean pass-by-value semantics, or copy-values-for-the-caller implementation? If you mean semantics, then yes: FP languages tend to like value semantics. However, I don't see what that has to do with referencing; you can certainly allow only passing things by value, but only pass references: that's what Python and C (and many others!) do, for example.
It sounds like you may be conflating pass-by-reference with passing references.
If you mean implementation: which ones? In the context of immutable and often even persistent data structures, copying them doesn't make a whole lot of sense.
There are advantages to both methodologies, but I must say I agree with you. The accepted academic practice is to find/develop the right formalism and then implement from there.
Despite this being akin to heresy, I much prefer to implement, then formalise later. I find it easier to "get the job done" that way.
Which functional language prevents you from passing buffers by reference?
Edit: I see there are quite a few. Please tell me about one :)
Alright ... I didn't mean this to be confusing ... here I'll remove that --- I was referring the whole class of languages which are more driven by purist theory than by practical need. There's quite a few that only support pass-by-value intentionally
C only supports pass-by-value (unless I missed something recent) and it's generally not considered a pure functional language.
C lets you make pointers to arbitrary things, so even if you somehow had a value containing your 1GB buffer, you could pass a pointer to it instead of copying it around.
sorry ... it's 340 am ... i'm screwing up my english. alright ... i'm going to stop replying.
Evidently "mostly functional" programming does work, as does imperative programming, because we've got loads of successful software written using these paradigms.
I'm absolutely all behind the idea of looking at what the future of programming languages is going to be, and how we're going to cope with diverse, highly parallel systems, and how we can reduce errors, bugs and failures. But it will be a while until we figure that out.
It's a shame he's put an introduction to monads smack down in the middle of this otherwise nice argument. If there was ever any objection to pure functional programming it would be that monads are complex and hard to learn and reason about. Putting an explanation of them with all sorts of mathematical terms is not helping the cause.
Not only are monads not the only solution to doing I/O in a pure programming language (stream based I/O or functional reactive being another), they need not such a mathematical explanation.
An I/O monad is a simple class modelling a box. There's two functions for it, one puts something that's not in a box in a box. The other function allows you to give it a function that is applied over the function in the box, without taking it out of the box (this is called a 'bind' operation).
Note that there's no function to take the value out of the box.
The trick is that the only thing that knows how to get the value out of the box is the VM itself. So its task is to at the end of your function, execute whatever function created your monad, and then execute all functions you gave it using the 'bind' functions sequentially over the returned value (which might include unwrapping more i/o monads).
Since every bind function relies on the previous value of the monad, it can naturally only be executed sequentially.
Note that this explanation does not explain monads either in full or very accurately, it's just one of the patterns that monads are used in, and hopefully gives you an idea about how it is that monads are used to enforce encapsulation and sequential execution of effectful functions.
His argument is that concepts such as laziness lead to unexpected results in imperative languages. However, such problems also occur in pure functional languages:
http://stackoverflow.com/questions/5892653/whats-so-bad-abou...
I would like to point out that there are classes of laziness. Haskell's laziness guarantees that an expression will not be evaluated if it is not used. e.g. take_first(42, 42/0) will not return an error. This enables you to construct infinite lists of primes and such.
Some classes of functional languages had lenient evaluation, evaluation is still lazy, but all expressions will be evaluated at some point in the program. You cannot do the infinity tricks, but you can still write the nice recursive functions, those that are used to show off the benefit of laziness.
The Haskell type of laziness bites you in the ass when trying to do parallel evaluation, which is why you need the rpar/rseq constructs to avoid opening the nasty trapdoors that regular evaluation will not hit.
Even besides I/O and parallelism the laziness can bite you. Mainly, it can make it hard to predict memory/processing use, unless you are intimately acquainted with the innards of Haskell.
The author never explicitly specifies what "does not work" actually means. Does it mean the code doesn't run? Is riddled with bugs? Certain types of reasoning aren't possible? Certain compiler optimisations aren't possible? The design of such languages is more inconsistent than they'd like?
"Computation is not just about functions, if computation was just about functions then quicksort and bubblesort would be the same because they're computing the same function. A computing device is something that goes through a sequence of states. What an assignment statement is doing is it is telling you "here is a new state". Functions alone don't solve the problems of programming because programs (on the whole) are non-deterministic; on the other hand, imperative programs are using assignment statements to compute functions, and that's silly."
That exact phrase was said to Erik Meijer by Leslie Lamport when Erik interviewed Leslie. [1]
It seems clear (if you take Leslie's word as gospel) that there is room for 'Mostly Functional'. That is, accept that your program goes through a series of states, but use pure functions to calculate those states.
However, as an imperative programmer who's found Haskell over the past few years (thanks, in a large part to Erik Meijer), I happen to think that Haskell has it just about right.
[1] http://channel9.msdn.com/Shows/Going+Deep/E2E-Erik-Meijer-an...
I must say that in all of my years, most every piece of source I've ever seen has been mostly [insert paradigm here]. OOP is "mostly" more times than not, same goes for FP. I would argue that "mostly" does work, and thankfully so, because nearly every service that you use throughout your day is a "mostly".
"Mostly functional", in my eyes, works as well as contract-based specification of interfaces works. The property of being free of (surprising/undesirable) side effects is part of a contract that can, but should not be violated by an implementation. To me Meijer's argument sounds like a strawman, since the alternatives to encapsulating side-effects in the terms of a (informal, software) contract are all not very appealing.
Declarative sublanguages such as LINQ work precisely because of this "contract" of being mostly-functional, even if the work behind the scenes (database accesses etc.) certainly changes the state of the world.
This would be a better article if it didn't implicitly assume mutable state was always bad. For example, locally mutable state (that doesn't escape) is highly unlikely to bite either you or the compiler.
OK, this points out a couple of things that "do not work" in these sorts of situations:
- lazy evaluation with side effects - replacement optimisations based off of referential transparency when there are side effects - general side-effectiness
This is basically saying that functional programming doesn't work when it isn't functional... but we can still have functional "chunks", and monads _are_ an effect system.
I don't really understanding what he's trying to prove, of course purely functional semantics fall apart when side effects are introduced.
The article should start by defining what he means by "work". I see mostly functional programming working every day. I even see non-functional programming (grasp!) working every day.
"It is impossible to make imperative programming languages safer by only partially removing implicit side effects" So when we can call a program safe? My personal definition is that a program is safe when it always gives the right output for the input it was designed for. Of course if you put water in your car won't make it run, the same way if you provide the wrong input to a program might end up in a wrong output. Getting back I do believe in the middle way.
Erik's example of the using statement is a straw-man: you get exactly the same problems if you do this:
using(var file = File.Open(path)) { _someField = file; }
..and then use _someField in another method.
It's not so much a problem with understanding closures, but of the limitations of IDisposable.
I don' understand where he sees the problem in the first C# example? I believe it actually works, the interleaving is OK to happen IMHO. I guess in Haskell it would also happen, as it's also lazy. What am I missing?
I use Scala for "mostly functional" programming, and it works very well. Sorry, Erik, you are just not where it's at anymore.
Happens to be that Scala is Erik's favorite as well.
I must say that the article has a completely different tone than when you'd talk to him personally, though he likes to talk in extremes and only afterwards talks about what is practical yet not in a diminished importance kind of way. I interpret the article just as a big indicator to a fundamental problem how we can express stuff in language and their accompanying downsides.
Talking in extremes is practical, if you want to be heard and/or start a discussion. See this very submission :)
Lisp is mostly functional. Arc isn't purely functional, and it works quite well.
Another monad introduction.
All I get is a 403 Forbidden error on this link
I think the real failure of FP has been demonstrating its advantages in practical terms. While deferring impurity is admittedly interesting (if you care about the theoretical underpinnings of programming at all) what do I gain as a programmer?
I'll try to illustrate my point:
In programming we often have to do something more than once. In terms of mental complexity, I'd rank our options like so, from easy to hard:
Loop -> Recursion -> Fixed Point Iteration
We can do the same thing with all of them. Recursion in some cases can lead to a stack overflow and any fixed point iteration has the disadvantage of, well, being f-ing hard to understand.
A beginner can get a `while` or a `for` loop in seconds. They're very, very intuitive. Once you figure out solving problems in software often requires doing things repeatedly, loops open up a lot of power.
Recursion is more difficult once you throw in conditionals and return values. They're harder to reason about. Loops are simple. So why bother with recursion? I learned recursion because it exists and I love learning. Could I have written just as much software without it? Yes. Would it have been just as functionally correct, maintainable, easy to read? Yes.
I know very little about fixed point iteration. Why? Well, because I have loops and if I'm feeling jaunty, recursion.
Having said that, I recently came across this article about the Y combinator:
http://matt.might.net/articles/implementation-of-recursive-f...
I recommend that article for everyone. But the most important part is the section "Exploiting the Y combinator".
> This formulation still has exponential complexity, but we can change it to linear time just by changing the fixed-point combinator. The memoizing Y combinator, Ymem, keeps a cache of computed results, and returns the pre-computed result if available:
The author is talking about his algorithm for calculating Fibonacci numbers. While the naive recursive approach is limited due to its computation complexity, by caching intermediate results we can short-circuit the calculations made.
> The end result is that the 100th Fibonacci number is computed instantly, whereas the naive version (or the version using the ordinary Y combinator) would take well beyond the estimated lifetime of the universe.
I don't know enough about what's being done here to say that this optimization couldn't have been made with a loop (I believe Turing equivalence proves that it could have been) but at least I get to see some of the magic that fixed point iteration gives us.
Haskell, as a functional language, offers a lot (purity! type safety!) but the community hasn't really shown how these things are valuable. Type safety is great, but is Haskell materially better than Objective-C (my primary language) in that regard? Pure functions are trivial to test, but in a language with a reasonable amount of type safety, how many individual functions are getting tested anyway? Of course I can write pure functions in an imperative language and often do. And I can do so without painfully abstract concepts like the dreaded monad if I want to do something as pedestrian as generate a random number.
The reality is writing software for most people is about the platform they're targeting, available libraries, ease of use and techniques for dealing with long-term complexity. I have yet to find a reason to use Haskell even though I would love to -- more accurately, I have yet to find a situation where I could justify using Haskell.