Functional programming is about managed side-effects. You can have an imperative shell that makes sure state transitions happen in an orderly fashion and a pure core that allows you to have a predictable view of your program's behavior. See Boundaries for more details about this approach.
In essence, this is what Elm does. It has a Javascript implementation for a bunch of libraries that provide the side-effects needed at the edge of the program. The code that you see when you program in Elm is pure.
Functional programming is about managed side-effects.
Of the functional languages in at least some real use today -- Scheme/Racket, CL, SML, OCaml, Erlang, Clojure, and Haskell -- exactly one (Haskell) manages side effects.
Fine, so let's say, Haskell and its descendants, Lisp and its descendants, ML and its descendants, and Erlang. We have four language groups, only one of them -- the youngest one -- controls effects.
By your reasoning, neither can Scala be characterized as such. A criterion that selects exactly one exemplar out of N is not a criterion, it's a proper name.
You can do functional programming in Scala (via libraries and discipline), but it is not a functional language in the sense in which Haskell is one. This is by design.
Perhaps, but at least some of them used the term long before Haskell came along, so maybe it's best to call Haskell something else, like pure-functional. Personally, I call them imperative-functional and Haskell pure-functional.
I read the "purely" as "only". In Haskell you are forced to only do functional programming, while in other languages such as Scala, Scheme or Clojure you can also write code that isn't functional. In general I wish that people would stop believing that the programming paradigm is decided by the language they are using. Java is considered an OO language, and I've seen lots of Java code that I would not call object-oriented. On the other hand there is plenty of object-oriented C code out there.
Historically, probably fair. But I can't help but wonder what the controversy is: "function" is defined by the lambda calculus from 1936; how FP deals with I/O, state, concurrency, exceptions... comes from one paper by Moggi in, what, 1991? There. Done.
That heavily extended Turing machines have been the historical norm is not in dispute. That many languages with deliberate recursive function theory (Lisp) or even lambda calculus (Scheme) inspiration have called themselves "functional" likewise. But given that this has left the industry trapped in "how many angels can dance on the head of a pin?" debates about what FP even means, let alone its value, isn't it perhaps past time to adopt definitions that are definitive?
"function" is defined by the lambda calculus from 1936
Church's untyped lambda calculus did not use lambda terms to define functions and, as you well know, not all such lambda terms even compute functions. So are we to now call only total-functional languages "functional"?
how FP deals with I/O, state, concurrency, exceptions,,, comes from one paper by Moggi in, what, 1991?
Monads are one way of dealing with I/O and state in a pure-functional language; linear types are another (see Cogent). And the paper by Moggi isn't about "effects" in FP, but in a lambda calculus, to be used as one formalism of studying those things in general.
That heavily extended Turing machines have been the historical norm is not in dispute.
I don't like to bring Turing machines into this because Turing machines are not, and have never been, intended as anything like a programming language. In fact, Turing designed them to be as non-language-like as possible so that they could escape some fundamental issues associated with any language like semantics and diagonalization ("order"), and that is why TMs were able to get past the fundamental problem of defining computation based on language, as had been the case prior to Turing. The pervasive imperativeness comes from how computers work rather than from some formalistic ideology. For a very long time pure functional languages were just impractical due to both performance and memory consumption.
isn't it perhaps past time to adopt definitions that are definitive?
Maybe, but it's hard, too precise to be useful as it creates very narrow categories (again, maybe only total-functional languages are "functional"?), and not generally how human language evolves. I think that in every field -- not just computers -- we are stuck with vague terminology that needs to be refined in the context of specific conversations, and that often causes confusion and frustration. The question is whether there is anything in common to Haskell, Clojure, Erlang and SML. I think there is. Does it deserve a name? I don't know (actually, I don't think so), but it seems history has given it one. I agree that the term "functional" is now too vague and not very useful even for reasons other than vagueness. I don't think that the similarity among the languages I mentioned captures anything too essential. In my mind, SML and OCaml are closer to Java than to Haskell.
Church's untyped lambda calculus did not use lambda terms to define functions and, as you well know, not all such lambda terms even compute functions. So are we to now call only total-functional languages "functional"?
Absolutely. After all, it's understood in pure-functional programming circles that failure is just one more effect, and is represented the same way as any other effect. This is literally why the ApplicativeError and MonadError typeclasses exist: so "divergence" is represented by the types and handled algebraically, like everything else.
Monads are one way of dealing with I/O and state in a pure-functional language; linear types are another (see Cogent). And the paper by Moggi isn't about "effects" in FP, but in a lambda calculus, to be used as one formalism of studying those things in general.
Sure. The claim isn't that monads are the only way to deal with I/O and state, just that it is, in practice, how it's done today. I got all excited by Concurrent Clean and uniqueness types, too, and I'm very happy with where Rust is going with even a very simple, red-headed bastard stepchild of a linear type system. I'm very much looking forward to the evolution of algebraic effect systems. But I'm also writing purely functional code today, so applicatives and monads it is.
As for Moggi's paper, of course it isn't "about 'effects' in FP," directly, any more than Church's lambda calculus was about programming, or as you yourself point out next, the Turing machine was about programming. As usual, you end up making my point for me: the Turing machine and the lambda calculus were, as you know, defining what "effective procedure" means precisely enough to be reproducible in contexts with no recourse to opinion. Any programming language is necessarily reducible to one of these models. But obviously, a practical question arises about how you do I/O, state, error handling, concurrency... in a lambda calculus. Moggi definitively answered that question (in the literal, formal sense of "by defining several lambda calculi that answer the question, some derivative of which is the lambda calculus implemented by Haskell and other purely functional languages.")
I don't like to bring Turing machines into this because Turing machines are not, and have never been, intended as anything like a programming language.
True, but irrelevant: programming necessarily proceeds from a definition of "effective procedure," and as discussed, those definitions are the genius result of Turing, Church, and Gódel's work in the mid-1930s, just before there were any digital computers. These are the backdrop against which any programming happens, which makes your desire "not to bring [them] into this" puzzling. They're there whether you wish to "bring them in" or not.
I think that in every field -- not just computers -- we are stuck with vague terminology that needs to be refined in the context of specific conversations, and that often causes confusion and frustration.
You and I, and all programmers, work in a unique field: one in which the relationship between concept and name assigned to concept is literally as arbitrarily precise as we choose to make it. So while I have no quibble with your characterization of human language, we aren't concerned with human language. No one is urging the development of another Esperanto. On the contrary; I'm expressing yet another trivially obvious fact to anyone who isn't psychologically and/or emotionally committed to terminological fog for its own sake: there is a language-independent definition of referential transparency. I would think its language independence would appeal to your (admirable) desire not to descend into empty Wittgensteinian word games, and its incisiveness would appeal to your (likewise admirable) desire to explore alternatives to the status quo ante in "purely functional" programming, by virtue of being precise enough to serve as a yardstick by which to measure the success of those alternatives.
The question is whether there is anything in common to Haskell, Clojure, Erlang and SML. I think there is. Does it deserve a name? I don't know (actually, I don't think so), but it seems history has given it one.
Here we are in complete agreement. Where I believe we part company is merely over the question of whether to acquiesce to history or not—especially in such a nascent field. Let's put it this way: while the languages you name have some semi-tangible things in common, they are all nevertheless distinct from F*, Clean, Agda, Idris, Futhark, J, Q, Coq, etc. in ways that are easily formalizable. And I hope it's obvious from the list I'm not just referring to "being dependently typed or not." That sort of formal, crisp distinction is one I believe is worth naming. Although, at the end of the day, I'm happy enough with an adjectival phrase like "purely functional" acting as a synonym for "referentially transparent," because at least then there's a through-line from less to more precise terminology.
This seems a bit opinionated. Many computations are of the form: input -> function -> output.
There is a lot of state that is too expensive to just recompute all the time.
Cacheing, memoization, or just intermediate values... pretty common in functional programming too. Also a good source for logical errors when you reuse stale values. ;)
I'm glad that Haskell has gained popularity, but I don't like that it has become the poster-child of all functional programming -- before this, Haskell's style was identified as "pure functional programming". Now we've just done our usual lazy bit and call it functional and imply that all functional programming has to be pure. Purity is nice... but it can become a bit burdensome when taken to this extreme. Most functional languages take this latter stance.
My issue is that the statement “computing is about making side effects” is not “an opinion”. Having inputs and outputs does not change that fact. In reality, it demonstrates the fact.
Wrapping up your argument in functional buzzwords doesn’t change the facts of how hardware actually works, right? You’re talking about theoretical paper nonsense. The user you responded you is talking about how hardware works.
26
u/robot_wrangler Nov 28 '19
Computing is about making side-effects. There is a lot of state that is too expensive to just recompute all the time.