Anders Hejlsberg: You Can't Maintain Large Programs in JavaScript.
css.dzone.com
css.dzone.com
There is no reason to lock the browser in a cage half-designed and half-implemented by an engineer under a tight deadline over a decade ago.
http://research.microsoft.com/apps/mobile/publication.aspx?i...
Doesn't Google make some internal use of a tool chain that compiles Java to JavaScript? And then of course there's Dart and CoffeeScript. I'm sure there'll be more.
My sense is that the people that suggest javascript as a compilation target really don't want competition for javascript in the browser.
Not really, because most of the complication and mess is in the document object model, not the language syntax. How do you propose to solve that problem?
> My sense is that the people that suggest javascript as a compilation target really don't want competition for javascript in the browser.
You sense incorrectly. I'm not working in that area, I don't have a dog in that fight. I just think it's a good idea to point out problems with a proposed solution before somebody goes off and puts a lot of work into trying to implement it.
Look, if javascript, with all it's insanity, can be standardized across browsers, for the love of pete, a simple stack-based VM can be too. And then everyone can go to hell in their own way.
Admittedly, people will stop using Javascript the second there is a viable alternative. I regard that as a feature.
Get over it already.
Brendan Eich:
"If I wanted a safe intermediate form, I would look at arithmetic coding techniques that self-verify (so that you can't express other than well-formed programs), not bytecode and not JS source. It's IMHO very unlikely that TC39 will standardize a bytecode for JS. An AST encoding has better chances and better properties including self- verification and view-source-ry."
We should have things like JVMs for the browser (cached on the client-side, no doubt), but we should also have non-portable code for people who want to do things that cannot be done portably. A bytecode standard cannot be all things to all people, because some people need to do things that can't be done on some platforms.
If you really want everything to move to the web you need to expose vector math instructions for video encoding, you need to expose CUDA for scientific applications and so on. You need to be able to ship machine-code to the client where it can run (properly sandboxed, no doubt).
Of course, if you want everything to move to the web there are bigger problems to overcome - HTML and CSS aren't really suited to application development, the filesystem APIs are not sufficient, and there's still the problem of getting GUI applications to compose well...
A lot of the problems would go away if the following was part of the language and present in every browser:
* namespaces/modules
* a sensible OO system people would actually enjoy using
* the ability to require/import/include a file/module
* the functionality Underscore.js provides
Then, at least the basic structure of the code would be consistent across programs from various people, and I wouldn't have to work out the details of yet another OO system, yet another way of just laying out the code across functions/objects each time I want to read another JavaScript program or yet another way of doing function () {}.bind that works across the browsers (here the problem is also the long time it takes for the majority of people to install a browser new enough to adopt the revised standards). And it's not only about reading code, how do you write an object-oriented refactoring tool if every program realizes OO in its own distinct way?
The situation is probably slightly better with server side JS where you are free to adapt the newest version of the language/runtime and also the server-side frameworks at least to some extend tend to encourage to use a common structure for all the modules.
Require.js solves point #1 and point #3. Backbone solves point #2. Underscore solves, well, point #4.
In my experience, the other important thing to maintain coherence and sanity while creating large JS apps is to have a system that makes dependencies between modules very, very clear. Require.js basically also does that for me; it requires every module to define their dependency on other modules on top of its file.
I highly recommend people creating large JS apps to at least check it out : http://requirejs.org/
The first is basically a web framework that lets you compose a fully working website from a whole slew of independent, pre-built, unique components. Each component is made of Require.js modules (like its own HTML, CSS, JS). This isn't exactly the largest project, but Require.js really comes in handy with its ability to conditionally load modules (or components, in this context.) because you simply won't use all the components in the system, and also its ability to treat pure text file as regular modules (for HTML and CSS part of the components).
The second is a visual drag-and-drop web builder that lets you visually build a working end product of the first project that I mentioned. This is a large project simply because there's usually quite a lot of that you have to do in order to make a completely visual system work. I simply can't imagine building this system without explicit module dependency that require.js enables.
This is true in any language that supports modules, not only OOP languages.
I don't think you could grow to that size codebase with Javascript.
Taking it to an extreme to demonstrate the point - a good team working on a large project could also just implement a new, more expressive programming language than Java and use that to implement their application.
The whole point of language selection is to begin with a more capable toolset.
Dog dog = new Dog();
I'm lazy and I find that sometimes expressing myself in JS is just what the doctor ordered. But with the lack of a type system, I can get pretty frustrated.
Java was created (or adapted to) for the benefit of consultants. Therefore it requires complicated frameworks and you can not manage coding Java without an IDE. All this makes it easy for consultants to get long paying gigs.
Edit: thanks, downvoters! You are incredibly naive...
Nonsense. I write a lot of java in emacs and use no frameworks at all. It's a simple systems programming language with an excellent standard library and great concurrency support.
While many people do abuse Java by plugging AbstractFactoryFactory objects into their framework for procedural programming, that's an issue of bad programmers rather than the language itself (which Java has lots of).
Maybe it is possible to use Java in a sane way. But there are for example best practice guidelines by Sun/Oracle. If you try to follow all that stuff, it will be painful.
Also most libraries for Java were written other Java developers. For example the other day I tried to send a simple Twitter update with Twitter4J (I think that was the name, might mistake it with another one). After an eternity trying to figure out how to configure OAuth with the AbstractAuthenticationProviderAuthenticatorCredentialsMessUpperInterfaceImpl (which requires clicking through several levels of the API dopcs to get the hang of), I eventually gave up and just used a 10 lines Ruby script instead.
Twitter twitter = (new TwitterFactory()).getInstance();
Status status = twitter.updateStatus("hello from Twitter4j");
You also need a 4 line twitter4j.properties file containing your assorted tokens and secrets. It's a bit longer than 10 lines, but still pretty straightforward, even with the use of a (probably unnecessary) TwitterFactory.By the way, I said java is a simple systems programming language. It's not what I would reach for if I wanted to update twitter either.
What do you mean by "systems programming"?
Redis <- systems programming
Aggregating your tweets in redis <- application programming
So presumably it's the culture you don't like, rather than the language.
I disagree. Any substantial JavaScript application can reach this size and more. It all depends on the level of abstraction you choose and the functionality frameworks and libraries provide out of the box.
I have written numerous mobile applications in JavaScript and they quickly sprouted to 10-30kloc. The majority of this being API functions to speak with our robust REST API, custom controls extending those provided by the Ext framework, and controller logic.
A bit OT, but it might prove the point. I'm a Groovy enthusiast, and have done this exercise a few times. I'll give a presentation of Grails - basics of Grails, basics of Groovy, then demonstrate a Grails app. I'll walk through all the functionality of the app, then ask the Java developers in the audience for a guess as to how many lines of code they expect the app takes.
I clarify I'm asking for lines of code that I had to write, not necessarily in supporting libraries and such. I still get estimates from the Java devs of 25-30k LOC most of the time. Sometimes someone will be brave enough to estimate 15k. The real answer is closer to 6k.
Have you asked Grails/Groovy developers the same question? Have you written equivalent Java code to compare outright?
I agree on the point that people may just be bad estimators, but I was assuming at least some of them have done approximate LOC counts on their own code from time to time to get estimates. I do.
So I've been using "factor of two" as a rough benchmark. Of course, this assumes appropriate library support; if most of the C code implements a priority queue while C++ has that as part of the standard library, then the comparison is invalid.
I don't know if for this Grails comparison if the extra factor of two comes from Groovy or from the effectiveness of Grails over whatever the Java programmers would use for web app development.
Surely he must have a FactoryOfFactoriesFactory class in there to make factories of his factory factories. That's at least 1k LOC right there!
However, there are many classes of code and #2 applies more to some than others. If you are writing something algorithmic like Peter Norvig's famous spell checker[1], the primitives of your language make a big difference. On the other hand, if you are writing something that is larger but of lower complexity where your code is more like configuration of a bunch of pages, this makes less of a difference.
Also, it's at least possible that the people who see your code assume a higher standard of maturity than your 6kloc has. We've all tried creating an apparently complete app rapidly, only to work on it for ages afterwards, implementing fault tolerance, graceful degradation etc. etc.
Sure, the lines end up less wide, but while you gain something by using function() over inner classes, you also end up reinventing package declarations and visibility all the time, so I don't see a clear advantage of one over the other.
(notice: I do understand people will generally do things in Java that they won't in JS, such as adding setters and getters, but I have seen that done in plenty of "big" js libraries, so I doubt that counts.)
> Everything must be an interface/impl/dao/controller so you get 4 files + hibernate mapping (if not using annotation) + view files. Eventually you have 10 files for every object.
> Mid-stream decisions (we need to go from Struts to Spring) that effectively create two separate in-process branches of the code base.
> Java developers building their own castle. Some guys "owning" a side of the application and not communicating therefore creating solutions their own way instead of using common libraries.
Specifically to your description I would argue whatever you are doing must be in multiple projects or it has been poorly engineered. If it is in multiple projects why couldn't another language take it on?
Which makes Java more dynamic at the cost of making it as hard to refactor automatically, as any dynamical language.
The part of code that doesn't use reflection is the part of code, that would be easy to refactor, even if it was written in javascript.
void foo(Reader r) { /do stuff with r/ } ... foo(new BufferedReader(new FileReader("/log.txt")));
There, I just did dependency injection without a trace of reflection. Additionally, since everything is statically typed in Java, refactoring is still vastly simpler than with a dynamically typed language. The few spots in the code where you might be trying an invalid type cast are just that - a few spots. Such problems don't permeate the entire codebase as they do with a dynamic language. And I'm saying this as a huge fan of Clojure and Groovy, preferring them over Java on the JVM.
[edit: changed "strongly" to "statically"]
var decoration = { topmargin: 10, bottommargin: 10 };
...
function setMargin(where, value) {
decoration[where + "margin"] = value;
}
....
setMargin("top", 20);Scala with IDEA was tricky. But then I'm pretty poor with Scala, so it must be me!
Do you mean "iterate"? Because refactoring has little link to innovation.
What about rapid prototyping? Perhaps the biggest strength of the lighter, more dynamic languages is that they require much less work to play around with ideas in the early days. On the other hand, if you can’t then tidy up the code that sticks, to make it into something that is more systematic and maintainable over the long term, you’re either going to waste effort rewriting it or you’re going to take on ever-increasing levels of technical debt, and either way your progress will be slower.
Refactoring really, truly and absolutely has nothing to do with rapid prototyping, nor does rapid prototyping has any use for refactoring.
> you’re either going to waste effort rewriting it
Throwing one away isn't "wasted effort", it good practices: you created a turd the first time around and learned a lot, polishing that turd would be wasting time.
Contrast this to refactoring huge javascript or python programs because the compiler isn't going to check if what you refactored 'fits' into the place you put it. It's always very nerve racking for me when refactoring large pieces of javascript or python because even if it runs, often many bugs remain lurking that won't be found out until the unit tests are re-written.
var myModule = require('./module');
instead of import com.company.module.Sth;As for my reply above: "you should not have that problems" does not make them go away: some problems solved by software are inherently complicated and cannot be reduced to some simple API calls or a 5kloc code base. Composition of 100 multiple 5-kloc code bases still makes them a 500-kloc code base with all mutual dependencies.
Just saying "you should not write so much code" might display inexperience with problems which fall outside of the currently common javascript apps ...
The worst is even that you (or somebody else) begins with good intentions ... and then , 4 software generations and law changes later, the complexity starts to smell.
Heijlsberg argues, that javascript will be harder to refactor in those cases. I can't really disagree with him.
So it find it's not so much lacking thought. The current thought for some is "let's use javascript", let's see what comes of that... :-)
We haven't really figured out how to write large complicated applications- Static analysis is no magic bullet, and neither are dynamic languages. I think what we are seeing is just a kind of physics of the mind- reactions and re-reactions to unpleasant experiences we've all had, and are trying to avoid, which leads us to polarise on the static/dynamic divide.
When writing code, sure. When reading and/or maintaining code, not so much. Especially when it stretches across function or method boundaries. Languages like C++ and C# have the right happy medium in my opinion: `auto`/`var` keywords for type-inferenced local variables, but explicit parameters and return types for all methods.
For example, suppose `f` is a function of type `int -> int -> int -> int`. In python, semantic differences are obvious between:
a = f(x, y, z)
b = functools.partial(f, x, y)
Where as in OCaml this difference is glossed over: let a = f x y z;;
let b = f x y;;
Even though the names "f" and "x, y, z" are relatively evocative of their types, the two expressions are very different without any particular indication as to why.That's why I think that there is a happy medium, where syntax is very helpful in determining types such that there is no need to give explicit types to "a" or "b" in these examples, but there is enough information explicitly available to declare that "f(x, y)" is an invalid function invocation, not a curried function of the type `int -> int`.
It could be you're rationalizing from a first principle ("it must be equally easy, so if it has not caught on it's because programmers are not used to it").
Probably because that's the paradigm of the most popular languages, the first paradigm everybody learns in college and university and likely the first language a person will encounter? Because most programmers are despite what they would say about themselves in public, actually stubborn language bigots? I would think about that because it puts functional programming (or logic programming (or proof-based programming (or stack-based programming))) at a disadvantage and a fair conclusion cannot be derived from uneven exposure.
Consider the actual research done in this area, classes have demonstrated that whatever the language, there is always the same distribution. Those that excel (small group), those that succeed through hard work and do good (most of the group) and those that just don't get it and fail (small group). This has been shown to be true for C, Visual Basic, Pascal, ML, LOGO, Scheme (SICP!) and even Coq!, which is a proof-assistant. Probably Prolog, too, but I haven't looked at that. You can easily google this, just search for things like "lecture/semester findings" and "student reactions/performance" and insert your language of interest.
That is just a rephrasing of "they are used to imperative programming". The question is why.
>Consider the actual research done in this area, classes have demonstrated that whatever the language, there is always the same distribution. Those that excel (small group), those that succeed through hard work and do good (most of the group) and those that just don't get it and fail (small group). This has been shown to be true for C, Visual Basic, Pascal, ML, LOGO, Scheme (SICP!) and even Coq!, which is a proof-assistant. Probably Prolog, too, but I haven't looked at that. You can easily google this, just search for things like "lecture/semester findings" and "student reactions/performance" and insert your language of interest.
How does that work in real life though -- when pragmatic issues get into play that are absent from "implement this algorithm in whatever language".
I answered it. They are used to it because it's the first paradigm there was and being the first thing gives you a good leg up.
I personally didn't use it because there is not enough people using...
Some OCaml: https://github.com/kig/preludeml/blob/master/src/prelude.ml
Should it be titled: "Anders Hejlsberg: I Can't Maintain Large Programs in JavaScript." ?
Let me maintain my larger program in JavaScript. It will require smarter staff, but the application will be more flexible and change more quickly than that same large program written in Java or C#.
For us the proper place for JavaScript is the browser, just to do some DHTML stuff.
It has 0 and -0! The default namespace is global! It has a === operator, for crying out loud!
For folks who spent a bunch of time writing AbstractPatternVisitorHandlerFactory classes, it probably looks like the end of the world at the hands of a bunch of freakin' degenerates.
2. The fact that pathological-condition-X of a language is in theory understandable with sufficient study is a total red herring in a broad discussion of that language.
Today, I spent five minutes tracking down a
SyntaxError: Unexpected token u
It was honestly hard to read this as "don't give an un-itialized value to this function or it will end up being passed to JSON.parse that will happily treat undefined as a string".I'd take a NullPointerException with a proper stacktrace everyday.
SyntaxError: Unexpected token u foo.js:123
h foo.js:456
g foo.js:789
f
The issues are* no reference to JSON.parse which is where the error occurred
* the error is not "object has no attribute toString", which would have made it clear what it's happening, but something else derived from having silently coerced "undefined" to string, which obscures what is happening.
As I wrote, it was a five minutes thing, not hours, but it's five minutes I would not have spent if it had been:
TypeError: Cannot read property 'toString' of undefined: [native code]
JSON.parse [native code]
h foo.js:456
g foo.js:789
fBy now I'm sure everyone has seen the WAT talk (https://www.destroyallsoftware.com/talks/wat) which illustrates some of the more obscure (and funny) coercions.
It's ok to say that JS's type coercion is a misfeature, good programmers learn to not throw out the baby with the bathwater.
Rewritten as a general programming language "smell". (That said, I do enjoy me some Javascripting, and I have a side-project with it.)
The data structures are lacking and have bizarre corner cases. I don't know if what it features as OO can be called OO and I don't care much, but the abstraction mechanisms are inferior to what I'm used to. Performance is unpredictable and sometimes I'm forced to write less readable code. Refactoring is a scary adventure, debugging is subpar, code navigation is cumbersome..
The things that I do like about it are availability and that it's cross-platform.
Given those circumstances, the amazing thing is that JavaScript has held up as well as it has.
The problem with JavaScript is not lack of 'classes', but a not just dynamic but also very weak type system.
Add to that the callback-hell caused by the async model of things like Node.js, and you end up with something very hard to maintain quite fast.
What I really miss from a maintainability point of view with javascript, on the other hand, is better development tools (ie IDEs and testing tools). I'm pretty sure there is a big opportunity there.
Dynamic, weak typing makes writing static analysis/refactoring/etc tools extremely hard, but more importantly makes nearly any compile-time guarantees impossible. Having your app crash because of a small typo of a member name is impossible in C++/C#/Java. It happens to me almost once a day with Node/JS.
No-one should even be thinking about unit tests if they are not already using a language in which all code paths can be evaluated and type-checked at compile time. Why get a dog and bark yourself?
And you should do this with any language, typed or not.
On the other hand, pylint for Python is really good for static checking, even if it won't catch everything.
(I don't know anything about the GP, but I really haven't looked at Java development in 5++ years. (-: If nothing else, I might need to update my jokes :-) )
Annotations have the disadvantage of needing a recompile to reconfigure?
In any good development shop you make a change, your CI server picks it up, builds it, and creates a release candidate you deploy. Every change should be checked into source control and built by your automated build bot.
At that point the difference between changing an annotation and an XML configuration file should be moot. I'd think the annotation would be easier because you can still mess up the syntax of the XML file pretty easy (e.g. "Can you change the blahblah label to System & such?" That ampersand will hose your system when you get it to production).
But that's a tiny subset of what architecture astronauts try to label "configuration".
But to me its not really a critical issue, because OOP is very easy in CoffeeScript, which is my favorite language and happens to compile to JavaScript.
Not that javascript doesn't. It doesn't have the sugar around it, and it doesn't statically check them. But javascript's OO is not "prototype-based" (that doesn't even make sense, prototypes are not a unit of reuse in Self) and it's not anything even remotely close to Self's.
Javascript's OO is single-inheritance class-based with no `class` sugar but at the end of the day it behaves pretty much the same way (and offers no more features — less if anything) Python's or Ruby's class-based OO work (sans metaclasses).
I have quite some experience with most mainstream dynamic languages, and wouldn't do any large scale application with any of them.
The company I work for, does large scale enterprise consultancy projects.
This means:
- Multiple development sites;
- Some projects have up to 200 developers;
- Usually several megabytes of source code;
- Continuous Integration build systems with automatic unit and integration tests
- Usually distributed architecture
- High performance requirements
No way I would advise anyone to do develop such systems in a dynamic language, let alone in JavaScript.