I like strong typing and compilation errors
Today I had to modify a piece of JavaScript code. The code used return a single string, and I needed to modify it to return an array of objects. Using C#, it would have been easy, change the return type, hit the compile button, fix the errors, rinse & repeat until it compiles.
In JavaScript code, however, it was much more complex, I had to find all the places where that method was called, and that particular parameter would pass unchanged throughout several functions before it was used, so I had to track that down. Pretty annoying.
And since I know that I’ll get questions on that, here is the actual example:
getDocument: function (id, operation, successCallback) { $.ajax({ url: settings.server + 'docs/' + id, dataType: 'json', complete: function(xhr) { switch(xhr.status) { case 200: var data = JSON.parse(xhr.responseText); var etag = xhr.getResponseHeader("Etag"); var template = xhr.getResponseHeader('Raven-' + operation + '-Template'); successCallback(data, etag, template); break; case 404: successCallback(null, null, null); break; } } }); },
I needed to change the template variable from to a dictionary of headers, since it needed to be processed elsewhere.
As for where it was actually used, here is one such example, which shows several layer of indirection (because of continuations):
Comments
The problem is very simple. Strong typing and compilation errors are implicit unit tests that you never have to write. When you go dynamic, you have to unit test these things if you want any kind of regression protection.
duck typing is indeed a double edged sword
@pete: I wouldn't say the problem is the "duck", but more the "dynamic", or "weak". You can have strong static duck typing as well.
Maintaining javascript rarely is a pleasure (very oppositely so to writing it in the first place). I usually try to stack functions not more than 1 level deep, but from what I can see you are writing them directly in the literal, which leads to this (difficult to read) style.
better IMHO is
var api = {}
(function(){
}());
Amen. Used to do nothing but dynamic languages for years. So happy in strong typing land. Sure most strong-typing languages could benefit from more inference, but that's no reason to go dynamic
I totally agree. Although many devs would argue dynamic typing can be argued as a feature as it provides less friction within the language. I personally find that the lack of strong-typing affects the ability to have a good IDE (like VS.NET/R#) with rich re-factoring support. You're basically reduced to find/replace re-factoring which ultimately increases the risk of changing your code without introducing bugs only discoverable at runtime.
Personally I think the descriptive design-time API and rich intelli-sense support it provides out weighs the disadvantages of statically-typed language. But thanks to the 'dynamic' keyword in C# you can almost have the best of both worlds. The remaining major advantage I see with the dynamic typing in JavaScript is that you can run your code instantly without compiling.
Though at the same time I do love JavaScript for HTML UI's, I couldn't imagine another language better suited to dealing with the painful W3C DOM. It's protoypal roots make it a flexible enough that you can build OOP-like constructs on top of it and allows for some rich API's like jQuery's powerful $() which lets you build succint, elegant solutions which I envisage would be a pain to do in a statically-typed language.
ActionScript is about the worst mix dynamic and static typed features you can get. As a statically-typed language, it lacks any added niceties like type inference which actually makes it more verbose than C#. Then forfeits the major benefits of a dynamic language where the compile times are madness, we had an application that would take 5minutes to build from a single code change, and they make the default to build automatically with every keypress? lame.
Dynamic typing isn't mutally exclusive with strong typing. Ruby, for example, is dynamic-typed and strong typed. Strong typed just means that an object knows and holds onto it's type (Ruby, Python, C#, Java vs C). Dynamic typing just means that you can change the type of a variable at runtime:
x = 1
x = "a"
Often, dynamic languages also have other features such as meta-programming or open classes, but those are orthogonal. Weak typing does make it harder to have real refactoring support, but dynamic typing doesn't hurt it. The original refactoring IDE was the SmallTalk environment, which is a dynamic, strong type system.
I find it interesting that the first point brought up was about unit tests, because you still need to unit test this code. Even if it were C# or Haskell, you still need to test it. Static typing only catches one class out of an infinite number of classes of bugs.
No offense Ayende, but I think what you want here is better tooling and better design. I don't know if better tooling exists for JavaScript (I think it does, but I don't know), but design could have been better as pointed out by Jan. I'd also like to point out that JavaScript has optional static typing :)
@Chris
Exactly what I was thinking. JS was always hard, until I figured out how to write unit tests for it. Still I'm happier writing C# code where I can refactor and really lean on the compiler. Absent that unit testing is all the more important.
You don't have to declare the anonymous functions. You can used named functions with the call and apply methods rather than declaring anonymous functions. Makes it easier to refactor and keep track of in IDE's that aren't JavaScript aware.
Jim: re: optional static typing - in what sense. That was on the table at one point for ECMAScript 4, ActionScript 3 was based on an early draft of ES4, but I believe it's been thrown out of ES5.
@Jim Deville
I think we can all agree that the earlier you can catch bugs the better. With R# you don't even need to compile as it does the syntax/error highlighting for you while your writing code. It even let's you know if you're using a wrong variable by highlighting un used ones, when conditional logic is always true/false, when your code path uses an object that you've tested for nulls, etc. I estimate I would've saved countless hours of debugging these otherwise runtime bugs if it wasn't for R# pre-compilation hints. This level of error checking is just not possible in a dynamic language.
Unit testing in JavaScript is still pretty weak, the best I've found is the YUI test framework, but even then you have to do a fair bit of plumbing to make it work for you. Although one difference I do find is that because JavaScript is completely asynchronous the tests are lightning quick where as soon as you load the page all the tests have already run :) But I believe that's got more to do with asynchronous-style programming that the dynamic/static nature.
VS.NET still provides very good tooling for JavaScript the only one I know that has rich intelli-sense support + documentation anyways. The problem that I always run into is if you have a syntax error somewhere then you lose intelli-sense support and it becomes very hard to find where the syntax error is.
Check F#. Statically typed yet has a very powerful type inference system. Altho, annoying sometimes.
Dynamic typed languages can be a real pain in the..
You might want to reverse the dependencies instead of the user interface driving the app have the document drive it.
var docHandler = function(id, operation, successCallback) {
}
var withId;
withId.id = 12;
withId.Edit = function(successCallback) {
}
withId.ShowEditor = function (/function paramters/) {
//gobley gook
}
withId.Edit(function(doc, etag, template) {
});
@Scott Koon:
I thought that JavaScript supported it, but looking around I'm pretty sure I was wrong.
@Demis:
How is R# not possible in a dynamic lanuguage? syntax, easy. unused variables, possible, not 100% perfect, but it can still warn you. conditional logic: don't see why it's not possible. Nulls should be possible too. Yes, you'll have to sandbox certain methods to analyze them (eval, define_method, class_eval etc in Ruby), but it's not impossible.
Again, the idea of refactoring was from a dynamic language (SmallTalk), Ruby has passed what is known as the Rubicon for refactoring (refactorings that require AST analysis). The only common language I think would have trouble here is Perl due to the fact that it cannot always be parsed before runtime.
The fact that JavaScript, Ruby, etc don't have better tooling support is due to business needs. Same with the test support for JavaScript. Personally, I prefer Ruby's test frameworks to any I've ever used in static languages.
Jim, dynamic typing is not also changing type of variable at runtime but also defining variables of undefined type. Your sample is pretty much valid for C#
x = 1
x = "a"
where's x is System.Object
and also it's valid for C (depending on compiler, actually)...
Couldn't agree more. I think Michael Feathers summed it up beautifully when he said:
"A language's type system is the set of tests its designer could come up with without looking at your program."
http://twitter.com/mfeathers/status/10292408755
I have to agree with you all. This is getting really frustrating, especially when trying to refactor a large layer in PHP or any other dynamic typed languages.
@Ray: That's not the same thing. What about this:
x = 1
x = "a"
y = x.ToLower()
Can't do that in C# if x is typed object. But you can do that in a dynamic-typed environment. Just change the type of x to dynamic and it works in C#.
The arguments I've heard for using dynamic typing are typically quite weak. In reality, with strong typing, you don't only get a nice, strong IDE and compiler that can debug things for you - you also get a design that can be analyzed without taking into account all possible third party alterations, and a whole host of simplifying assumptions about the design.
One aspect that I was told of before was that being able to add your own methods to an object that has already been defined allows you to modify the object so that you can use it directly. But for the code that the person was building, those added methods would not be used (at all) by the framework in which he was working - which is the same as saying, "I'm tacking on my own stupid responsibilities onto this random object in the system because I'm too lazy to figure out how to add it correctly."
To be honest, a lot of this dynamic typing craze lately drives me pretty nuts. As a developer, having a dynamic language makes it much easier to hack a system that's already built, but at the same time leaves the dev with the least amount of useful information in exchange for some mediocre flexibility. I don't mind having it in the language just in case, but an entire language that's dynamically typed just makes me angry and sick to my stomach. Call me old-fashioned!
@Ayende: if the value is passed untouched all the way, why would you care to change that? you only need to change where you create it (in the jQuery .ajax call) and the usage in the final func in the continuation. All the rest would just work, because of the dynamicism.
Ken,
The problem is:
It is touched in some places.
I need to figure out WHERE I am touching it.
There are multiple callers for getDocument
I feel your pain. While I really really enjoy C#, I hate Javascript programming. It is such a headache. I avoid it all costs.
@Jim Deville
They're are a number of reasons why its hard to provide rich intelli-sense support for JavaScript:
You can't validate type errors since it is legal for a variable to change types in the language.
JavaScript has a problem where ';' are optional which hurts syntax checking since you're lexical parser needs to be liberally support different inputs.
arguments in functions are optional so it's hard to tell if you're using the method as it was intended or you've left an argument out.
JavaScript has a concept of null, undefined, NaN, false which potentially mean different things in different contexts (e.g. when used as a bool all mean false). which one do you want the IDE to validate against? The existence of the '===' operator is a symptom of this.
Can't see how you can reliably determine un-used variables/types without executing the program. JS lacks a compile-time definition of the identity/definition of your types/variables in your program as they are actually defined at runtime. So accessing and creating variables can only be determined when your program is actually run.
Don't get me wrong I still love the expressiveness and functional nature of JavaScript, but if it was so perfect google wouldn't have wasted their time building GWT which actually uses Java (one of the most painfully restrictive statically-typed languages there is) to generate JavaScript. One of the major technical advantages touted is to be able to use a mature IDE with rich re-factoring support:
www.scribd.com/.../GWT-the-Technical-Advantage
Its no coincidence that a language as ubiquitous as JavaScript still lacks an IDE with good re-factoring support - this isn't an oversight, this is just hard to do.
I think my point was that you shouldn't have multiple callers. getDocument should be making the calls. it knows what it needs.
Joseph,
getDocument is a low level API call, it is being used in a lot of places. The issue is that I needed to change it output, which naturally propagate out.
In a strongly typed language, I would get immediate type mismatch errors, with JS, I had to do text searches
I think this whole issue is mainly due to a poorly chosen JavaScript Editor. We have just completed a very complex UI in JavaScript and jQuery and this would not have been possible using VS2008 / VS2010. The intellisense is just not on a workable level.
If you use IntelliJ however, you get full intellisense for javascript AND all of the refactorings. Coding JavaScript in anything else is just plain silly.
Next to that, read JavaScript the good parts and leave all the parts out.
@Tom de Koning
Well this is proving to be quite an informative thread, had no idea there were better JS IDE's than VS.NET - it does get really slow at times for large code bases that I wish I could just turn it off. I guessed Jet Brains owns this space but I figured all their best efforts was only starting now and being put into 'WebStorm' (is that the best to go with in the future?), had no idea IntelliJ had killer JS support. I noticed others have recommended ScriptSharp here as well, which also looks worthy of consideration.
I'm interested to know what other Ajax frameworks / tooling you recommend? Do you use FireBug, WebDeveloper toolbar, etc. What's your preferred browser to develop with? What unit test framework do you use? Is there other libraries Ajax devs should know about?
IMHO Nobody would use Javascript if there was an alternative. But there isn't still a real alternative
Someone once said (and I forget who) that the compiler is just a glorified spell checker. I think I would agree
Like all dynamic languages you only get both the advantages of dynamic typing and refactoring safety if you unit-test your code.
Writing unit-testable code and actually writing the unit-tests is generally a lot easier in dynamic languages. It is quite an effort to write decoupled code in a statically typed language so that it can be unit tested. Then you often end-up leaning on a mocking framework which is typically using reflection, emitting IL and casting-up types internally anyway!
I'm not saying I can write good, unit-tested Javascript and feel comfortable when refactoring it. But I put that down to me not having yet developed the skills and discipline, and not a particular language type system. These are skills and disciplines I want to learn because I like programming in dynamic languages including Javascript.
@Gian Marco Gherardi: Many people use Javascript on the server where there are plenty of alternatives. See Mozilla Rhino, V8, node.js and MongoDB.
@tarn
That's not my experience, I find writing unit tests in C# to be a lot faster and require less effort than in JavaScript. It definitely helps if you naturally write testable code - typically this just means to use an IOC or other form of dependency injection. Using a mocking library like Moq makes Mocking and testing a non-event.
NUnit tests also have the advantage of being supported and runnable within the IDE as well as external automated build tools and CI servers like Team City.
Type safety is false safety. The only safety you can have is achieved through unit tests, which you would do in both, static and dynamic code. This is the only thing that protects sanity and business logic in your code. This is the only thing that allows your code to be maintainable and expandable by other people.
Tooling and unit-tests make any platform / language equally tolerable. My personal thing: Despite coding on a C# project in VS.Net08, I always open JS files in NetBeans. It picks up all unused variables (which means, i know where i misspelled a var), makes even "manual" refactoring JS a breeze with automatic highlight of the used var and has a solid IntelliSense-like and syntax-checking systems.
If there would not be good tools for working with C#, the voices of people pissed at static typing languages would easily overshadow the static aficionados cause remembering all the dressing for the code (type names, variations in methods etc) is insurmountably painful. Thankfully good tooling for JS exists, just like it must have existed to make static side of .Net a viable dev platform. You are complaining just because you did not bother to find tools.
The subject of "safety" fallacy and tooling deficiencies aside, there is a qualitative chasm in mentality between dynamic and static language aficionados. That's the only true, and very divisive difference.
Although Python, JavaScript and C# are essentially the same languages and I get them all, I'd rather chew sand then roam and fight the similar-but-ever-different cloud of .Net classes. The thought of subclassing from an Dictionary to have another custom object so I could have a method i would get for free in one-and-only standard Dictionary type in Python is dreadful.
Doing the Reflection dance so that i can iterate over properties of an object is dreadful. Knowing that Reflection exists is a criminal waste of a mind. Knowing that for all objects in JavaScript obj.method() = obj"method" singlehandedly justifies JavaScript's existence and superiority over anything statically-typed.
I understand and empathize with your pain, just because I harbor a similar pain. Yours is against dynamic typing. Mine is against static typing. The trick is to understand that it's not the poor language's fault. The problem is only in your head and is due to your habits.
@ayende,
Even with unit tests instead of just failing at the template object. You would have failed in the consumer objects of the template object also. This would act like a type safety compiler.
This would be a similar issue if you were returning mock objects. Then it would require you to use an integration test to make sure that the template object was dealt with correctly in all the consumers.
As a metaphor I've been using 'operational programming...boring shit to combat training' MATRIX.
I've actually been running into this working on a Legacy PHP Application.
But dammit JavaScript has closures this should be a non-issue.
I need to spend some time thinking about this. Maybe I need to think about how this problem be resolved in Common Lisp or an event driven system.
Try haXe for a fantastic javascript like language, with strong/inferred typing, generics and other goodies which compiles to Javascript for client and server (node.js using type signatures you can find here http://github.com/blackdog66/ ). haXe also compiles to PHP, Flash and C++.
I find you can get that quick iterative development using TDD, except TDD is even more powerful than static compilation, since you use the runtime to do your bidding.
I recommend using a good spec framework, such as Jasmine ( http://github.com/pivotal/jasmine), JSpec ( http://github.com/visionmedia/jspec), or Screw Unit ( http://github.com/pivotal/screw-unit).
Daniel,
In statically typed languages there is no need to always have 100% code coverage. In dynamically typed there is such a need.
For statically typed (modern like C# or Java) IDE will catch for you most of stupid mistakes instantly, in dynamically typed you MUST run ALL the code to be sure it's working.
Huge difference in my book.
The interesting bit is that I am really fascinated by the number of people who assume that I have no clue about dynamic languages
I find it interesting that so many people mention IDEs. Is that the only reason C# is tolerable to people, because we have Visual Studio?
I like strong typing and I cannot lie
All you other brothers can't deny
etc...
@Copenhas
I believe 'elegance, productivity, performance and power' are c#'s main appeal. For most things I'm able to achieve a lot more in a lot less time and effort than with any other language or platform. Having good perf is a welcome bonus.
Having a good feature-rich IDE that can complete your coding sentences definitely increases your productivity.
AFAIK C#/.NET is the only language that have added features primarily for being able to provide better support for IDE's and code-gen'ing, e.g. partial classes, partial methods, etc.
Me too !
I completely agree. Sadly, my current position involves a good amount of PHP, and I have to maintain some very large legacy applications. It is PAINFUL. I miss good old resharper, compiler errors, castle transaction management, nhibernate, Rhino ESB, etc. sigh
I, too, like static typing--a collection of fast unit tests implemented automatically by the compiler. To me, most of the benefits of dynamic typing are gradually disappearing with newer features of statically-typed languages (such as compile-time reflection).
you problem of finding the usage can be addressed by using javascript unit testing such as QUnit. Running the test suits can point to where the code is broken - just a thought.
Must be eat, drink, sleep - tdd/ut guy. ;-)
There is also an alternative to script# with name DotWeb.
http://github.com/flaub/DotWeb
It transforms IL to Javascript instead of directly parsing C#.
Comment preview