Scheme (and FP in general) is much more than just first-class functions. If that's all that is required for a language to be considered functional, then nearly every modern programming language is a functional language, and the term becomes essentially meaningless. First-class functions are nothing special or unique anymore.
FP is also about frictionless composition of functions, declarative rather than imperative style, use of expressions instead of statements, and very critically, emphasis on statelessness and immutable data.
JavaScript is anything but stateless--it is extremely awkward, if not impossible, to avoid mutation in idiomatic JS code. Imperative statements abound, and everything is a reference, there are really no pure values to be found. Functions can be composed, but the syntax makes it unintuitive and ugly. And though it is probably not a hard requirement for FP, most FP languages at least make an attempt to distinguish pure functions from stateful procedures, e.g. via types in Haskell, or just naming conventions, as with Scheme's `set!`, but JS does not really facilitate this in any way.
Functions and lexical closures are undoubtedly some of "the Good Parts" of JavaScript, but these alone are not sufficient for functional programming. JS is fundamentally an imperative, OO language which happens to have a very light dusting of FP flavor on top.
Scheme (and FP in general) is much more than just first-class functions. If that's all that is required for a language to be considered functional, then nearly every modern programming language is a functional language, and the term becomes essentially meaningless. First-class functions are nothing special or unique anymore.
If you'd like to express this simply, I've been trying to get people to explain functional programming as programming where the idiomatic method of computation uses the lambda calculus as a base model.
When it comes to mainstream programming languages it's often easy to "see" the lambda calculus or a Turing machine behind the computation.
If you grok the lambda calculus, this is indeed a simple explanation. In my experience, most people who don't know what functional programming is also don't understand what lambda calculus is. In that case, all you've done is substituted one unknown bit of vocabulary for another.
I've also noticed that when I use unknown vocabulary in my explanations, it sometimes intimidates people away from asking more questions. E.g. "What is <some term I really don't understand>?" "Oh, it's just <another term, that the person you're asking seems to expect you to understand, but you really don't>". So I would avoid the term "lambda calculus" for the uninitiated.
I seem to have pretty good luck explaining what a pure function is (no side effects, same input always produces same output, referential transparency[1]) and going from there.
[1] Which, in turn, requires its own mini-explanation.
Possibly even more accessible: High school math. In a math exercise, when we state
let A = 2 + 3π
Then we can be dammed sure that for the rest of the exercise, we can replace "A" by "(2 + 3π)" (and vice-versa) without ever introducing an error. Likewise, functions don't change A's value behind your back no matter what. Something in, something out, period.
If I may, I'd like to log a difference of my intent with the word simple, as opposed to the word easy.
Simple is conceptually clean, while easy may be accessible to those familiar with related concepts.
My description was, I hope, simple: it has very high fidelity and is not prone to many of the misinterpretations of the other explanations. What it may not be is easy. One may indeed require further explanation to really understand the relationship I'm proposing. Sometimes, though, there is no shortcut to knowledge.
> If that's all that is required for a language to be considered functional, then nearly every modern programming language is a functional language, and the term becomes essentially meaningless. First-class functions are nothing special or unique anymore.
While I don't dispute the conclusion, this particular justification is worthless. You don't restrict the application of a term based on whether or not it'll become worthless due to over-application. IF functional programming is defined as "a language with first-class functions", then if "nearly every modern Plang has first-class functions" then nearly every modern Plang is functional.
That first IF isn't true, as the rest of your comment demonstrates, but term over-saturation is not why Javascript isn't functional.
Moreover, as far as Lisp dialects go, homoiconicity is a big part of their identity and a major source of their power. It's an orthogonal concept to functional programming in general, just look at e.g. Haskell, but that's one of the first things I think of when I hear the "JS is just an ugly Scheme!" argument.
Very true. Lisp's syntax is as much a component of its distinctive identity as its semantics. A language semantically identical to Scheme but without the homoiconic syntax falls far short of what Scheme is "about", despite what Doug Crockford might say.
javascript has json though, which doesnt make it homoiconic, but it allows you to mimic homoiconicity in a lot of cases. When I write in javascript, I tend to store my data in json and and write code that is generic, with much abstraction that has a small foot print to manipulate the json.
That is scheme-like. That is psuedo-homoiconic.
(If anyone in the know wants to add or refute this, I would love to know whether my non-cs intuition is correct here.)
(also it is still a very mutable language, even with coffeescript/underscore)
Homoiconicity is when the code of a language is represented in a structure that is primitive to the language. It doesn't have to do with how data is stored, it has to do with how the program text itself is stored.
The advantage of homoiconicity is that the language itself is manipulable as a basic type in the language. In Scheme, this means that it's easy to generate, analyze, and modify Scheme code using simple procedures in Scheme. e.g. I could (although I wouldn't) generate a scheme expression to compute an arbitrary fibonacci number with[1]
Notice that I'm not just pushing strings together to write code. I'm actually manipulating data structures in the language to produce and modify code. That is homoiconicity.
I prefer the term "semi-functional" for imperative languages that provide some support for functional programming.
It's a loose category, but I don't think it's completely meaningless. At a minimum it requires anonymous function expressions and garbage collection. That rules out C++ and (until recently) Java.
Some semi-functional languages have better support for functional programming than others, of course. JavaScript qualifies as semi-functional, but I agree that its syntax is not particularly conducive to the functional style. ML is a good example of a semi-functional language with very good support for the functional style.
FP is also about frictionless composition of functions, declarative rather than imperative style, use of expressions instead of statements, and very critically, emphasis on statelessness and immutable data.
JavaScript is anything but stateless--it is extremely awkward, if not impossible, to avoid mutation in idiomatic JS code. Imperative statements abound, and everything is a reference, there are really no pure values to be found. Functions can be composed, but the syntax makes it unintuitive and ugly. And though it is probably not a hard requirement for FP, most FP languages at least make an attempt to distinguish pure functions from stateful procedures, e.g. via types in Haskell, or just naming conventions, as with Scheme's `set!`, but JS does not really facilitate this in any way.
Functions and lexical closures are undoubtedly some of "the Good Parts" of JavaScript, but these alone are not sufficient for functional programming. JS is fundamentally an imperative, OO language which happens to have a very light dusting of FP flavor on top.