At least lists, dictionaries map relatively well to a tabular (SQL) format. Objects don't map well at all! Anyone who's spent enough time with "mature" ORMs knows this. Especially when there's a deadline and you have to write "native" SQL just to get whatever the hell you needed in the first place. "Well maybe you should have read everything and understood the ORM to its most minute detail..." NO! That's the whole point of abstraction! If I understood everything about that code, I'd be better off re-writing it to better suit MY specific problem. Look, I don't want to be another OO basher. OO definitely has a place in complex systems like game development, where the lives of the objects are longer than a page refresh. But in web dev, its becoming increasingly obvious to me that the OO paradigm is a huge time suck. /rant
I feel like we have this discussion at work daily involving nhibernate. It is an abstraction that makes 80% of work quicker and easier, but what it makes easier and cleaner would have been trivial anyways.
The term 'functional language' is not terribly useful these days. You can program in a functional style in many languages, just as you can program in an imperative style in Haskell or Scheme.
Last year, I wrote an article [1] talking a little bit about this, arguing that the notion of 'language paradigms' is mostly a crutch to avoid thinking critically about what a language is.
JavaScript has functional features (first class functions for one are done well) but I think that's about it. Side effects, mutability, etc all are allowed. Compare with Haskell, Elm, or Clojure where those things are either not allowed at all or discouraged.
The recent trend of "functional JavaScript" is mostly using third party tools that emulate feature from other languages (like Immtutable.js or Ramda), but IMO the core language cannot be called a proper functional language.
To be fair, everyone has their own definition of FP. For me, it just feels weird to call JS functional when it only has one "feature" of most functional languages. And at least in my college studies, the discouragement of side effects was stressed as the main feature of them.
Yup. The same is roughly true for being object-oriented.
The big paradigm divisions were largely a thing of the 1990s; since then languages have become increasingly multiparadigm, so that it's almost hard to find a language that isn't nowadays. I think Haskell and Golang might be just about the last remaining standard-bearers of the league of one-trick ponies.
Not the one you replied but I was taught through university and books that functional programming's most important feature is the discouragement of side effects. Even on Wikipedia it's said to be a style of programming that "avoids changing-state and mutable data". I'd put discouragement of side effects and immutable data up there with higher-order functions (HOFs).
You're more than welcome to have your own definition, but it's my opinion that it's too broad since HOFs exist in some form in languages like Java and Ruby and I wouldn't call them functional though like another commented said, languages are mixing paradigms nowadays (with the exception of Haskell!).
It is. Sadly the standard library isn't, but there are great libs. Programming Javascript with lodash/fp or Ramda is great fun, and in some places I prefer it over Clojure.
A functional language still uses procedures, even Haskell! To see what I mean, consider the possibility of divergence (infinite loop, `undefined`, `error "foo"`, you name it). It doesn't make sense for a mathematical function to diverge. The difference between, on the one hand, Haskell, ML and Racket, and, on the other hand, JavaScript, Python and Ruby, is that the former group encourages you program with procedures that compute mathematical functions, whereas the latter group does not.
> this doesn’t sound like the right distinction
It doesn't suffice, but it's a precondition. For good or for bad, mathematical functions are mappings from values to values, so you can't talk about computing functions in a language without a rock-solid notion of (possibly compound) value.
> and this doesn’t sound like the right example for (2) [deleted from your original post, but I'm not deleting it from here]
Yes, it's the right example. Haskell and ML prevent you from accidentally distinguishing two extensionally equal functions by not letting you compare procedures in the first place.
> and this doesn’t sound meaningful
These functions ought to be extensionally equal in any mathematically civilized language:
function foo(x,y) { return x + y; }
function bar(x,y) { return x + y; }
The nice thing about infinite loops, `undefined` and `error "foo"` is that nothing can use their result (i.e. nothing happens "after", although that's a tricky concept with lazy evaluation!)
In other words, Haskell doesn't have try/catch for things like `undefined`, so we do have to worry about them occuring, but we don't have to worry about them causing the code to misbehave; it'll either behave or it will stop progressing (either halting or looping infinitely).
On the other hand, the "IO" DSL built in to Haskell can do try/catch; but once you're in IO, all bets are off.
Oh, I'm not saying Haskell doesn't let you express functions. What I'm saying is that functions are expressed as procedures that compute them.
The nice thing about Haskell is that it distinguishes between procedures that may only compute functions (or diverge), and procedures with arbitrary effects.
Wikipedia is far from the be-all and end-all source, but at least it is one, so here’s a quote from https://en.wikipedia.org/wiki/Functional_programming: “It is a declarative programming paradigm, which means programming is done with expressions[1] or declarations[2] instead of statements.” I would agree with it that the usual meanings of “procedural language” and “functional language” are not compatible.
The quote says functional programming is a type of declarative programming, not the other way around. Prolog is irrelevant.
Backing up to equality as an aside, is there any language that will let you determine whether two functions are equal? If the functions that can be compared aren’t restricted to some silly level and the language is Turing-complete, I can’t imagine any better behaviour than Haskell’s that you already mentioned, which you can bring to JavaScript pretty easily.
> The quote says functional programming is a type of declarative programming, not the other way around.
Well, your quote doesn't even provide a full definition, sooo...
> Backing up to equality as an aside, is there any language that will let you determine whether two functions are equal?
No, that's obviously undecidable. (Rice's theorem.) But read what I said more carefully. Did I ever say “A functional language lets you determine whether two functions are equal?” I didn't! What I said is “A functional language doesn't let you distinguish equal functions.” Not letting you test functions for equality is a perfectly valid (and, in fact, the only valid) approach in a higher-order language.
> which you can bring to JavaScript pretty easily.
Not really. I can't undefined JavaScript's existing equality operator.
> a perfectly valid (and, in fact, the only valid) approach
I thought as much and it would really help if you would just say these sorts of things outright so the comments are actually useful instead of deeply nested wording quibbles.
With that out of the way: you can define a subset of JavaScript that satisfies your third condition by saying “don’t compare functions”. That’s why I didn’t consider it a meaningful condition.
Functional programming works best as a paradigm and then we can just call languages where it’s standard “functional languages”. Much simpler.
> I thought as much and it would really help if you would just say these sorts of things outright so the comments are actually useful instead of deeply nested wording quibbles.
I find it absolutely essential to make perfectly clear what exactly I'm refuting.
> With that out of the way: you can define a subset of JavaScript that satisfies your third
Second.
> condition by saying “don’t compare functions”.
That's a different language. Do you know of any implementation of it?
> Functional programming works best as a paradigm and then we can just call languages where it’s standard “functional languages”. Much simpler.
Nope. A functional language is one that makes it easy to write functional programs, by satisfying the conditions I gave above. A programming language is first and foremost its semantics, whatever the culture of its users.
> Nope. A functional language is one that makes it easy to write functional programs, by satisfying the conditions I gave above.
Except you’ve still given no source for the conditions, condition -1 (“a functional language is a procedural language”) is wrong, and a language satisfying the conditions in no way makes it easier to write a functional program just by virtue of satisfying the conditions (because they can be satisfied entirely by removing features you don’t have to use). Maybe you meant “harder to write non-functional programs”.
> Second.
The last item in a list of three items is not the second item.
> condition -1 (“a functional language is a procedural language”) is wrong
It's correct. In a procedural language, computations are structured as procedures that may call each other. In a functional language, that condition (plus three additional ones) hold.
> and a language satisfying the conditions in no way makes it easier to write a functional program just by virtue of satisfying the conditions
Removing pointer arithmetic makes it easier to write programs that don't contain memory errors. Removing spurious distinctions between memory blobs that hold the same value makes it easier to program with mathematical functions.
> (because they can be satisfied entirely by removing features you don’t have to use)
Removing features creates a different language. Often a nicer one!
> Maybe you meant “harder to write non-functional programs”.
Nope. That would exclude any general-purpose programming language. Lest you think otherwise: it's very easy to write imperative programs in Haskell and ML.
> The last item in a list of three items is not the second item.
> For good or for bad, mathematical functions are mappings from values to values, so you can't talk about computing functions in a language without a rock-solid notion of (possibly compound) value.
Erm, yes you can. You don't need "a rock-solid notion" of anything in order to talk about anything. It's certainly nice to have such well-defined concepts/terms, but requiring them is preposterous.
JavaScript doesn't have compound values. This is a fact. When you evaluate the JavaScript expression `{ first: "hello", second: "hello" }`, what you get is a pointer (or reference if you will) to an object with two fields. The object is compound, but the pointer is a primitive, indivisible value.
> The object is compound, but the pointer is a primitive, indivisible value.
So? Is the problem here that you can’t compare objects equivalent according to this with `==`? You can make a comparison to do this properly. (This is why functional programming as a paradigm works better and functional languages are just those where it’s the obvious standard.)
That's not the point! Even if I made a custom equality comparison that says two different objects are “equal”, it would be unsound to treat them as equal, because there exist other constructs in JavaScript that will let me distinguish them. For example, if I mutate one of the objects, the other will remain in its original state. This is exactly why a usable functional language must provide a good notion of compound value!
> Even if I made a custom equality comparison that says two different objects are “equal”, it would be unsound to treat them as equal
I didn't see anyone making claims about soundness. From a Curry-Howard perspective, all JS code is sound: it's unityped, so all programs are proofs of the trivial theorem "true".
From the perspective of equality functions/operators, you're right that it won't be sound; but so what? It's pretty much given that "Javascript foo" is a poor model of "foo", for all values of "foo" (function, integer, boolean, etc.); why should equality make any difference?
Just add "==" and "===" to your list of things to avoid, alongside mutation, random numbers, user input and other things which aren't referentially transparent. To be honest, equality isn't particularly bad if your code has some notion of types/contracts (whether enforced or not); e.g. it's perfectly safe to use "==" when you know that both sides will be strings, for example.
> For example, if I mutate one of the objects, the other will remain in its original state.
Mutation doesn't seem like a great example when discussing (pure) functional programming patterns.
> This is exactly why a usable functional language must provide a good notion of compound value!
You introduction of the term "usable" seems like a moving goalpost/no true scotsman.
> From a Curry-Howard perspective, all JS code is sound: it's unityped, so all programs are proofs of the trivial theorem "true".
Haskell and ML aren't unityped, but, in both, every type is inhabited by expressions. (Caveat: In ML, not all types are inhabited by values, but that doesn't matter, because programs correspond to expressions, not values.) So this doesn't distinguish them from JavaScript.
> From the perspective of equality functions/operators, you're right that it won't be sound; but so what? It's pretty much given that "Javascript foo" is a poor model of "foo", for all values of "foo" (function, integer, boolean, etc.); why should equality make any difference?
Equality matters, because the very first condition to determine whether a procedure computes a function is to see whether it maps equal inputs to equal outputs!
> Just add "==" and "===" to your list of things to avoid, alongside mutation, random numbers, user input and other things which aren't referentially transparent.
That's a different language. Do you know of any good implementations of it? And, FWIW, procedures that compute random numbers are totally fine. You can't rule out procedures that don't compute mathematical functions. You can only keep their use to the minimum necessary.
> To be honest, equality isn't particularly bad if your code has some notion of types/contracts (whether enforced or not); e.g. it's perfectly safe to use "==" when you know that both sides will be strings, for example.
In my day-to-day programming I manipulate more complex data structures than strings.
> Mutation doesn't seem like a great example when discussing (pure) functional programming patterns.
I'm not talking about pure anything. I'm saying that mathematical functions are value mappings, and if the language doesn't let you define compound values (tuples, lists, trees, whatever), then it can only conveniently implement very trivial functions - not enough for practical programming.
> You introduction of the term "usable" seems like a moving goalpost/no true scotsman.
I'm not moving anything. A functional language is one that makes it easy to write functional programs. The lack of compound values means that I'm given two unpleasant choices:
(0) Use Gödel numbers to encode compound values. Would you?
That gives you the <sarcasm>very pleasant</sarcasm> choice of working in a category without sums or tensor products distributing over sums! No thanks.
In practice, Haskellers don't think this way about their programs. Haskell's library ecosystem attests to the fact they think of Hask as some moral analogue of the category of sets, which has both sums and tensor products. And the worst sin a semantics can commit is to not reflect how programmers actually think about their code.
It would be reasonable to add _referential transparency_ to this definition and consider the implications. Say we have a function:
function time() { return new Date() }
Referential transparency implies that we can everywhere replace an expression with any reduction of it but because the return value of this function can change even when its arguments don't, this is not true in Javascript.
> Referential transparency implies that we can everywhere replace an expression with any reduction of it
I'm aware, but that's too strong a requirement. I wanted my definition to capture the bare minimum necessary to make it pleasant to write functional programs. The basic idea is “functional programming is designing your programs in terms of mathematical functions whenever possible (not always!)”, not “functional programming is requiring an embedded IO DSL to do anything effectful”. (But don't take this as a dismissal of Haskell. While I do have complaints against it, effect segregation isn't one of them.)
> but because the return value of this function can change even when its arguments don't, this is not true in Javascript.
That's okay. A procedure that returns the system date will never be referentially transparent, yet such a procedure is a necessity in any programming language.
My beef is actually with the fact JavaScript doesn't have compound values: https://news.ycombinator.com/item?id=12191119 . Since compound entities (whether values or mutable objects) are a necessity in practical programming, the harsh reality is that JavaScript programmers have to use mutable objects whether they want it or not.
> That's okay. A procedure that returns the system date will never be referentially transparent, yet such a procedure is a necessity in any programming language.
As long as it accepts a Universe or State parameter, a system time function is referentially transparent. This is rather like how every query in a database can be seen as implicitly depending on a transaction ID parameter.
One intuition that falls out of the Church-Turing thesis is that there is no routine in C, Pascal, &c that has no referentially transparent equivalent.
> As long as it accepts a Universe or State parameter, a system time function is referentially transparent.
Which begs the question - where do you get that Universe parameter from? Note that getting a single Universe parameter at the beginning of the program is of no use, because then you couldn't query the system date right now. You could only query the system date at the moment the program started.
You can't completely get rid of non-referentially transparent procedures in a general-purpose programming language. What you can do, at most, is banish them to a special sublanguage, like Haskell does.
> the Church-Turing thesis
The Church-Turing thesis is a statement about computable functions, but a procedure that returns the system date clearly doesn't compute a mathematical function.
> ...getting a single Universe parameter at the beginning of the program is of no use, because then you couldn't query the system date right now.
This is like saying, "...getting a single input at the beginning of the program is of no use..." but see below.
> The Church-Turing thesis is a statement about computable functions...
It is a statement about what is "computable", which is the alpha and the omega of what computers can do. If there is a Turing model for a particular "computation" there is a Lambda Calculus model for it.
A program that returns the system date operates on the machine state. You can treat this as "read from the tape" in the Turing sense or as input in the Lambda Calculus sense. The "output" can be seen as one of:
* A tape where the system states has been updated (since we took some steps, we updated the CPU counter, handled some interrupts, &c) and the system time has been written to a special place on the tape so the next computation can find it.
* A tuple of a new system state and the time value (picked out, again, for our convenience).
If we imagine the world "as a computer" then it is natural to compute a later state of the world as a function of the previous state. This approach actually works well in practice and it is not coincidental that this approach works with computers, since it is deeply tied into how they work.
Per Heraclitus, we never set foot in the same river twice. Treating the State or Universe as non-comparable captures this idea.
> It is a statement about what is "computable", which is the alpha and the omega of what computers can do.
The Church-Turing thesis is about what functions are computable.
> A program that returns the system date operates on the machine state. You can treat this as "read from the tape" in the Turing sense or as input in the Lambda Calculus sense.
Are you saying that the input tape may contain the later time at which a program will query the system's date? That seems like a physical impossibility to me.
> If we imagine the world "as a computer" then it is natural to compute a later state of the world as a function of the previous state.
The fundamental deficiency of this model is that it assumes you can put the entire world inside your program, and then get an entire new world as result. It utterly fails to take concurrency into consideration.
> The Church-Turing thesis is about what functions are computable.
Are you saying, that procedural programming can do something besides compute computable functions?
> Are you saying that the input tape may contain the later time at which a program will query the system's date? That seems like a physical impossibility to me.
When the computer retrieves the time from the clock, it does exactly that -- reads it from a location in memory.
> > If we imagine the world "as a computer" then it is natural to compute a later state of the world as a function of the previous state.
> The fundamental deficiency of this model is that it assumes you can put the entire world inside your program, and then get an entire new world as result. It utterly fails to take concurrency into consideration.
How does it fail to take concurrency into account? We can treat the world as being made up of many objects and evolve their state simultaneously if we like.
I've been a bboy off and on for almost a decade, good to see more people getting into it lately, dancing is the healthiest thing I do by far. Some unsolicited advice, there is more to it than just the moves, you'll enjoy it more/longer if you develop a deep appreciation of the music and culture as well.
For some reason this whole problem reminds me of early game developers dealing with small amounts of RAM. Which clearly isn't a problem today. So would it be fair to say we should focus on increasing bandwidth to most of the world. I'm not saying page weight doesn't matter, but if you're just trying to get something off the ground maybe you shouldn't worry about it so much. I mean, why worry about users with poor bandwidth, if you don't even have users? If you already have a growing user base, then by all means refactor, reduce the footprint. But if you don't, code the damn bloated thing first.
Sure, most games these days are probably more CPU/GPU bound, but then again people usually don't have more than one game running at a time, while caching the assets of thousands more.
Also, isn't adding bloat "more work"? And just like in real life, I think losing bloat is often harder than gaining it. Why not worry about carousels or endless scrolling or video background when even just one person misses that stuff? Where are all the highly successful websites that started to reduce bloat after they got off the ground? It's not a rhetorical question or sarcasm, I am interested, but I honestly can't think of even one example, it doesn't really mesh with my own (admittedly rather pedestrian) experience. A site starts with a blank design doc, and empty file and a white screen, and adding something and later removing it isn't easier than simply not adding it in the first place.
I feel like this could spark a massive bubble pop. I mean if investors have lost confidence in a company with one of the largest user-bases in the world, what does that say about all the startups that are valued so high and will likely never make a dime, unless they're bought out.