Why don't you like COBOL? Serious question.
Modern COBOL, or Ancient COBOL? Most people mean ancient COBOL, which is what I was talking about. I've heard modern COBOL is quite nice.
ES6 is a significant improvement for sure, but we aren't there yet.
In the meantime, a better question to ask, I think, is why all the JS love? What does it actually offer as a language, that others do not? The only thing that is universally acknowledged is that it's your only choice when targeting the browser, but that's not exactly a ringing endorsement. What other aspects are desirable?
(And please don't say "closures". Even C++ and Java have them these days, so it's not really an achievement - more of a baseline.)
-idiomatic higher-order functions
-relatively small (at least, until ES6)
-"this" (I kind of have a love/hate relationship with it, really)
-very little odd about the syntax (for a C-like language, everything is pretty much what you expect it to be, save some of the OO stuff, and the obvious elephant in the room that is ==).
-prototypical inheritance
-really easy serialization
-builtin regex support
-first-class functions that are equivalent to lambdas (you'd be surprised how many languages don't have this: It's pretty much just JS in the mainstream language space, AFAIK)
-A vibrant community, that, while not always exactly on target, is always doing somthing new and interesting, and constantly iterating on ideas.
-As of ES7, TCO.
Don't get me wrong, there's plenty wrong with JS: I'd say more than is wrong with Python, and less than is wrong with Perl, and there are some quite nasty pitfalls, but it's not a horrible language by any stretch, and certainly not deserving of its reputation as a hacky, bastardized byproduct of Brendan Eich's sleep-deprived brain.
Now if you want to see a REALLY stomach-churning scripting language, have a look at QuakeC. Carmack, I stand in awe of your brilliance, and I understand the necessity of hyper-optimization at the time, but are you sure you didn't contact some horrifying elder god to write this for you?
Mmm... I wouldn't really say that JS is a good example of idiomatic HOF - just look at the standard library. I would say that JS has specific idiomatic patterns (such as callbacks) that are a subset of HOF, but most JS code you see around is not like ML or Haskell with regard to HOF in general.
>> very little odd about the syntax (for a C-like language, everything is pretty much what you expect it to be, save some of the OO stuff, and the obvious elephant in the room that is ==).
And scoping. And magic semicolons.
>> first-class functions that are equivalent to lambdas (you'd be surprised how many languages don't have this: It's pretty much just JS in the mainstream language space, AFAIK)
Can you clarify? So far as I can see, all you need for lambdas are first-class functions (function-typed values), function literals, and closure semantics for the latter. Most mainstream languages offer that these days. C# would be a good example - what do you find limiting about its lambdas that JS does better?
>> Now if you want to see a REALLY stomach-churning scripting language, have a look at QuakeC.
QuakeC was basically just C with only primitive data types (no pointers or arrays!), and a single reference type that referenced a single struct type with an extensible definition. So yeah, not very impressive; but it mostly did what it needed to do in its niche.
Besides, it was fun fighting with the language limitations sometimes (e.g. you could simulate a global array by writing a function with a giant conditional inside - or, better yet, writing a code generator to do it for you). Again, not something you really want in a "production" language - but as far as writing game mods went, fun was usually the primary goal for most who did it.
Lodash/Underscore is tremendously popular. Yes, most JS libs HOF merely for callbacks, but the IDEA of, "foo takes a function and returns a function," is a fairly widespread idea in the JS community: The same is true of ruby, and a few other mainstream languages.
>And scoping. And magic semicolons.
Scoping is semantics, not syntax. And yeah, scoping semantics suck. But hey, IIRC at least they were never as bad as python's used to be :-).
However, this is no excuse for how awful the semantics are. Variable Hoisting is the devil. However, ES6 seems to improve this somewhat.
As for the 'magic semicolons,' JS doesn't do ASI when the syntax is ambiguous. So I fail to see what's so magical about them.
Unless you mean the semicolon/lack of semicolon on the end of a function definition. And that's for the same reason as C struct semicolons, and about as easy to remember.
>Can you clarify? So far as I can see, all you need for lambdas are first-class functions (function-typed values), function literals, and closure semantics for the latter. Most mainstream languages offer that these days. C# would be a good example - what do you find limiting about its lambdas that JS does better?
<rant>
First of all, people talk closures like they're some magical entity: They're not. They are a natural consequence of lexical scoping and every lexically scoped language you have ever used has them. Yes, even whatever one you're thinking of right now. They may not support inner functions, but all a closure is is a lexically closed environment, so semantically, EVERY function is a closure.
</rant>
My pedantic nature over that aside, yes, I will gladly clarify. I am not familiar with C# or Perl, and am not super familiar with the Java lambda semantics, so let's talk scripting languages: Ruby, Python, JS. And since this IS JS's domain, and Perl is Perl, the comparison should be useful.
Let's start with Python:
def apply(f, arglis):
f(*arglis)
It seems simple enough, but as you no doubt know, Python's lambdas can only have one statement: So while this works: >>>apply((lambda x, y: x+y), 1, 2)
=>3
This doesn't: >>>apply((lambda x: \
... if x:
... print("oh noes")), 2)
=>syntax error
Next, ruby. Off the bat, Ruby looks better: blocks, procs, and lambdas are everywhere!And thus begins our first problem: Blocks, Procs, and Lambdas are all SUBTLEY DIFFERENT. Bad, okay, but not awful.
However, Ruby is, unlike Python, fully Object Oriented. Thus, procs and lambdas are instances of their respective classes. What ruby should have done is introduce python style call methods, allowing any object to act like a function. Instead, much like Smalltalk before it, it added a call method to each object, which you call to invoke the code. This means that you can't just pass in a kernel method (What most of us call functions) instead of a block or proc when a function is expecting one: you have to wrap it. Thus, functions defined with def aren't the same as the anonymous procs you create: they're different entities. The Schemer in me is shouting that making functions and lambdas different entities should be a compiler optimization, not something transparent to the programmer.
Finally, Java: I said I wouldn't cover this, I lied. Java actually handles lambdas surprisingly well. The only problem is that a lambda can only touch 'final' variables. So a lambda can read the args of the function that created it, but it can't change them, and thus you cannot get the greatness of lambdas with mutable lexically closed environments. Joe Armstrong would disagree with me, but I'm not trying to build a 5 nines system with thousands of servers and billions of concurrent threads. Immutability is great, but a bit of mutable state every now and then is nice.
Basically, I like JS's lambda implementation because lambdas in JS are indistinguishable from functions created any other way.
Pretty much, these lambda semantics remind me of Scheme. The language I wish I was writing in. I mean, JS is nice enough, but if Spock ever becomes production ready...
>QuakeC was basically just C with only primitive data types (no pointers or arrays!), and a single reference type that referenced a single struct type with an extensible definition. So yeah, not very impressive; but it mostly did what it needed to do in its niche. Besides, it was fun fighting with the language limitations sometimes (e.g. you could simulate a global array by writing a function with a giant conditional inside - or, better yet, writing a code generator to do it for you). Again, not something you really want in a "production" language - but as far as writing game mods went, fun was usually the primary goal for most who did it.
Oh it's fun, sure. And then you see Xonotic. And you see OO hacked into a system that only kinda sorta supports it. Which is kinda cool, until you realize that that system is backing one of the biggest Open Source AFPS out there. And then you a bit ill...
I would say that it's getting mainstream in general. I'm working on an ASP.NET Core application as I'm writing this comment, and you'd be surprised as to how many functions get passed around and returned routinely. I was surprised myself.
>> Scoping is semantics, not syntax.
It is, but semantics is supposed to correlate with syntax in a sane way. In JS, it does not - you can place "var" inside a block, but it's not actually scoped to that block. If they required all vars to be declared at the beginning of the function, it would have made more sense. And I'm not aware of any other language that made the same blunder, except for old (pre-.NET) Visual Basic.
Python scoping is comparatively sane in comparison in a sense that the rules follow the syntax. You still can't scope to a block, but you're not misled to believe that you could.
>> As for the 'magic semicolons,' JS doesn't do ASI when the syntax is ambiguous
return
42
>> all a closure is is a lexically closed environment, so semantically, EVERY function is a closure.
True, but in common use, we usually mean "closures that can close over something other than the global environment". The other kind is just too trivial to be worth discussing on its own.
Regarding lambda semantics, I'll grant you that Python doesn't have full-fledged lambdas in that sense. But then again, they argue that if you need a multiline closure, it should have a name for readability purposes - I don't necessarily agree, but it's not an obviously invalid argument. And as far as expressiveness goes, named local functions provide the same degree of it, even if the code is more verbose.
On Ruby, I think the distinction between blocks, procs and lambdas is necessary if you want to provide various gradations of what "capture the lexical scope" means - and in particular, how you want to treat the meaning of "return", "break" and similar statements that, effectively, have an implicit jump target. In some circumstances, you may want to capture the target from the outer scope; in others, you do not. Still, they could have made do with two different types of closures, rather than three, one of which isn't even first-class.
FWIW, in C#, lambdas behave quite a bit like Java ones, except 1) they can mutate locals (which are automatically hoisted to the heap in that case), and 2) instead of implicitly implementing interfaces, the language provides function types (delegates), and a lambda automatically creates an instance of a delegate when used in the appropriate context. So you can directly pass functions to other functions etc.
(I oversimplify somewhat, because C# delegates are actually multicast, so they're not quite a bare-bones function type - but 99% of the time all those extras are unused.)
Then we have C++, where lambdas are also just objects with operator() defined for them, and no specific type otherwise (you have std::function when you need such a type, but idiomatic C++ is to use templates, so that lambdas can be inlined as much as possible).
I forgot lua before. Lua is pretty much javascript except without all the screwups and with 1-indexed arrays.
>It is, but semantics is supposed to correlate with syntax in a sane way. In JS, it does not - you can place "var" inside a block, but it's not actually scoped to that block. If they required all vars to be declared at the beginning of the function, it would have made more sense. And I'm not aware of any other language that made the same blunder, except for old (pre-.NET) Visual Basic.
Ah. Yeah. Well, that's why let exists now.
>return
>42
All right, I'll give you that.