Ask HN: Why choose a dynamically typed language?
And on the plus side, you get all the benefits of compiler type checking, better IDE support, and great refactoring support.
What are your reasons for choosing a dynamic language?
And on the plus side, you get all the benefits of compiler type checking, better IDE support, and great refactoring support.
What are your reasons for choosing a dynamic language?
In statically typed languages, at least the ones I've used, doing that requires a lot of overhead before the language will even let you run the damn thing. Yes I know lots of stuff is broken, but let me try out this one code path that I'm pretty sure works! Or if it doesn't work, fail at runtime so I know where it breaks! Instead, it won't compile at all, unless I go through an exercise of moving broken code out of the way (lots of block-commenting), supplying stub functions to take its place and the place of unimplemented stuff, keeping both an old and a new version of a function around so I don't break code that's looking for the interface with the old version (even though I'm not testing that code right now anyway), etc.
In Lisp, I don't have any of that commenting/stubbing/shutting-up-the-compiler hassle. If I want to try out one particular code path amidst an in-transition code base, Lisp will happily execute that code path. To me at least, it feels a lot easier to try out new ideas that way, because it doesn't feel like the code base is this ball and chain I'm dragging with me every time I try to make a change; I can choose to temporarily leave it behind for a bit.
I haven't worked with F#, but I have read a share of C# code. A non-trivial program is likely to need some methods to respond to many types; in this circumstance C# lets you write many methods, use generics, or (now I believe) use dynamically typed variables. Writing many methods is hard to maintain, and not at all concise. Using generics is theoretically great, but the syntax in C++/C# is just awkward and writing type-agnostic code with generics is nigh unreadable.
In Ruby or Python you have duck-typing, and you can easily be type-agnostic and concise; in fact if you are writing code in one of these languages, you /should/ be both of these things.
Duck typing is fantastic for true object orientation: "If it walks like and duck and it quacks like a duck, we can safely treat it like a duck." This reliance upon support for response to messages instead of reliance upon types is often the key to being concise and readable.
In ruby, for example, I can use object.responds_to?(:method_name) and be assured that the object will support this piece of the interface before I attempt use. This is an elegant solution to keeping code type-agnostic, and it emphasizes the best sort of object oriented methodology.
This is why I love Ruby.
Duck typing: http://en.wikipedia.org/wiki/Duck_typing Type inference: http://en.wikipedia.org/wiki/Type_inference
Type inference relies upon known, preset interfaces and inheritance from other types. Duck typing relies solely upon the methods being used.
In duck typing, this means that the user finds out at the time of use whether or not a method is supported by an object. Here, the "interface" to a type is not set in stone. It is inherently dynamic and as long as the message you pass is supported, (duck->quack), there isn't a problem.
The mental jump which many people fail to make is to understand that Object Orientation is about message passing, not types. The key questions are, "what message are you sending", and "does the receiver have a response" - it got dogmatized somewhere in small-memory static-land long ago that the best or perhaps only way to tell if an object will respond is through static definitions of complete interfaces, which came to be very strongly associated with our conception of a "type". Once the compiler has used it to check interface constraints are unbroken, this information becomes useless and unchangeable.
Ruby is an example of a language that does not use that means of answering the questions. Instead, an object has dynamic metadata about what messages it supports (specifically a hash of the method names), and uses that (malleable) data to generate an answer. It is fundamentally different than type inference in a statically typed language.
walk and talk of it and can walk and talk then as far as i'm concerned, its a duck because that is what a duck was expected to do.
duck typing is polymorphism w/o inheritance.
This doesn't even parse.
Duck-typing is the idea that an object's type at a particular time is made up of the set of fields and methods it has _at that time_. I suppose that doesn't strictly require updating, but it would be a pretty lame duck with it.
Polymorphism w/o inheritance is a fine description. How do you think that can be accomplished without dynamic updating?
hmm, if you have two objects which both support the method 'run', one of which is a jogger that will move into a 'running' state, and the other is a program which will actually begin executing code, and you have them both in say, an array that gets iterated over and every object is sent a 'run' message.. and they both take one argument and return nothing distinctive, well, that's extremely contrived. But such things happen. Scary.
I'm gonna start thinking about this. Thank you :)
Or you could be explicit about the interface you are writing the duck-typed code against. The problem here is that you can't assign an interface to an existing type, and adding interface declarations results in more code. It will be interesting to see whether this functionality (extension interfaces and inferred interfaces) makes it into C# in the next 5 years. My guess is that it's functionality which is possible to add, but we're not there yet.
Lets say you have a method the sole task of which is to call call .read() on its argument and return a single char.
char readch(obj) { return obj.read(); }
Now, since I have not declared any explicit type for obj, the compiler would generate code like this:
interface readch_obj {
char read();
}
char readch(readch_obj obj) { return obj.read(); }
So the function basically takes in an interface readch_obj which defines a single read() method that returns a char.Now, the trick is, the compiler can intelligently recognize that the FILE object has a method with the required signature (char read() ) and that the STRINGIO object also has a method with that required signature and so the compiler automatically tags the FILE object and STRINGIO object as implementing obj_readch and so, either of those objects can be passed in as an argument to readch().
Note: I'm not saying this is how the Go compiler actually works but this is the basic concept from what I understood.
The language doesn't need to allow for adding methods at runtime for this to work.
If the language did have the scaffolding needed to add methods at runtime, then yes, this technique wouldn't work and thats probably the fundamental difference between ducktyping and this (structural subtyping? not sure what it's called).
Then again, I haven't been keeping up with the more recent advances in compiler stuff, so maybe someone has already done this.
C# is a little better than the big 3 (C, C++ and Java), but you still trade off between helpfulness and verbosity. F# is a lot better IMO, but it's still fairly obscure, so of course not many people are going to be using it.
But anyway, in many cases, structural typing can make more sense than name-based typing.
Due to these tradeoffs, in some cases, people can be more productive in a language without the help of a typechecker than they can in certain languages' static type systems.
That's fine, but it doesn't help me with the errors that I make.
I don't make representation errors. I make kind errors. The difference is that the number of apples and the number of bananas are both non-negative integers, but a check for non-negative integers doesn't keep me from adding apples to my bananas inventory. Remember that I do want to add both apples and bananas to my fruit inventory.
Yes, I care about representation, but things shouldn't go south if I happen to have half an apple.
void DoSomething(Fuit fruit, uint quantity) { .. }
Inside that method you will be guaranteed to receive only apples or bananas and a non-negative integer for quantity.
Static vs. dynamic typing is nearly orthogonal to my programming language choice. Much more important to me are functional features. And all other things being equal, I'd rather have the flexibility of the dynamic languages than the performance and compiler-checking of static ones.
Haskell has some killer features that make me want to use it more. Static types are not on the list.
Maybe it's because all of my projects are small and I don't have to program defensively, but I've never been bitten by a type error that took anywhere near as long to fix than I would have spent using a static type system.
I'm sure in some circumstances there are dynamic language projects that are plagued by type errors, but in my case static typing is a solution looking for a problem.
The pattern matching syntax made a lot of things trivial to express that would require switch statements or if/else chains in just about any other language, so I loved that. Actually, once I got used to the function definition syntax, I like that too and felt it was very concise.
I like the flexibility that strings are lists (arrays?) of characters, which was incredibly useful for implementing a parser. I'm sure that would come in handy in just about any project.
The type system...eh. I did a bit of fighting with it which apparently is normal for newbies, and I can see the advantage of having it during the compilation stage from a performance perspective. It just didn't grab me by the collar like the other features did.
First, it's a lot easier to quickly figure out what a function does when it has a type signature. It's strongly self-documenting.
Second, it's easier to figure out what kinds of datastructures are used in a program, because you can see the types of the variables and functions you're working with. For me understanding the underlying datatypes is a big help in getting to grips with a codebase.
Third, the compiler catches a lot of dumb mistakes early. It's no substitute for testing, but a quick compile cycle will rule out a common class of errors and helps me make changes with more confidence.
It's not all roses and there is a price to be paid but I do thing there's a strong benefit.
I submit to you that I can show just as many examples where the type signature is of no help. Self-documenting functions existing in all paradigms, and it is a stylistic choice rather than one enforced by any technical considerations.
> Second, it's easier to figure out what kinds of datastructures are used in a program, because you can see the types of the variables and functions you're working with. For me understanding the underlying datatypes is a big help in getting to grips with a codebase.
Interesting point. In languages like Ruby and Clojure, you tend to use a standardized set of highly-optimized and standard structures as opposed to using generics to spin out a lot of them. They tend to conform to high level protocols (interfaces) much like statically typed languages do.
The flipside to this is that dynamic languages tend to have a lot more ease changing data structures down the road.
> Third, the compiler catches a lot of dumb mistakes early. It's no substitute for testing, but a quick compile cycle will rule out a common class of errors and helps me make changes with more confidence.
This is true, but I'd like to point out that lots of Lisp compilers already do this. They use optional static type inference for optimization and error checking but do not enforce those constraints, merely observe them and react to them.
Personally, I think that's the best of both worlds.
True, but what happens when you decide you really need a list of maps instead of a list of lists? You better hope you have good test coverage when you do this in a dynamic language.
I made a similar change in a fundamental datatype in a Scala program from a list to a symbol -> string map and once I fixed all the compiler errors the whole thing worked correctly on the very first run.
One of my projects for school turned into a 9k LOC Ruby project. I had precisely zero errors during the entire development cycle that static typing would've detected. Meanwhile, attempting a bit of dynamic code in VB.Net recently resulted in easily a couple hours worth of total waste dodging types while not going totally type-less. And then there's the crappy lambdas and total lack of anonymous or inner functions, so you end up polluting your namespace massively in many cases. (stuck in 3.5 + VS 2008, 2010 adds those) Gah!
I do occasionally miss IDE hinting, which is drop-dead easy in statically-typed languages. But dynamically typed languages allow such ridiculously simple APIs I find it's rarely necessary.
It's also still over 10x larger than the .Net project with which I've been fighting. And given my patterns of code, I see no reason why making my previous project 10x larger would have any more difficulties with typing than I had before. In a few years of Ruby coding, I can only recall a few immediately-identified type problems. Static typing seems to me to cost significantly more than its value.
You never get a
NoMethodError: undefined method `[]' for :foo:Symbol
or similar? Then you are either a much better programmer than me, or we are dealing with confirmation bias[1]. I get such errors regularly. They are trivial to fix and are caught early by tests/specs, but they are still there on a first iteration.There are definitely some downsides too though, and static languages have gotten a lot better in the last ten years. After many years of working almost exclusively in dynamic langs I'm enjoying having the help of the compiler again in the Scala hacking I've been doing.
Long Answer: For me, part of it is syntax. I have found that using Python has enabled me to learn some programming concepts that I struggled with in .Net. .Net supported them just fine, but somehow the Python approach was intuitive for me, so once I learned it, I could abstract it to a different language. Polymorphism being the primary example with Python.
More recently, I've gravitated toward languages that provide support with both dynamic types and static types (and arguably C# has heavily moved in that direction, but still I find the syntax not intuitive). The hybrid is nice, as you can worry about the higher level algorithm more easily with the dynamic feature, and then shift over to static to take advantage of the features you cite for static.
Earlier this year, I discovered the Cobra language (http://www.cobra-language.com), which boasts a python-like syntax on top of .Net (and supports both dynamic/static typing), and is optionally more strict than standard static typed languages in that it adds the benefits of contracts, nil-checking, and embedded unit tests.
I have used and continue to use both dynamically and statically typed languages and I find value in both. I do find little value in static type system that can't do type inference.
As to a couple of your generalities, a tour through smalltalk would do you well if you think that excellent IDE support and great refactoring are something that springs from static typing.
EDIT:
People talk a good deal about the + of compile time type checking. I have done tons of coding in statically typed languages like C, C++ and more and I think the compile has caught less than a dozen type errors for me ever and those were usually because I got lost in pointer hell. Most errors that a compiler would catch because of type has been in my experience something that there should be a unit test for and the unit test would have caught.
This kind of thing - a complete absence of NullPointerExceptions and segfaults - is what's great about static type checking.
That is part of the problem with the question... there are many different type systems out there that people would call 'static'.
I started using C# in 2001 and Ruby in 2004, and even after several years, I find I'm a lot more productive using Ruby.
This is my main reason - I can tackle projects with 2x or 3x less people on the team (which means I can also tackle alone projects vs. building a team of 2 or 3).
As well, I find dynamic languages more "fluid" to use.
This is changing. We'll see Haskell and Ocaml in the early 2010s, where Python and Ruby where in the early 00s. Still a little off the beaten track (as per the Python Paradox) but definitely usable. And if you're deploying compiled code, you don't even need any runtime on your target...
Question, what does compiled code have to do with dynamic vs static typing? You can compile dynamic code, you can interpret static code. The typing system or lack thereof, has nothing to do with compilation.
I remember those days when Python was yet to be accepted as a mainstream language. I wanted to use it, but I was writing software for our clients, who would deploy my code on 10,000 Unix boxes. This is back when an estate like that was a huge and unwieldy thing to manage. There was simply no way that our customers were going to roll out and commit to maintaining a whole new runtime across their estate just for me (where "me" is "my project"). So our realistic choices were Perl or compiled code. Nowadays Python has become a "normal" language and no-one bats an eyelid when I list it as a prereq. Took, all told, maybe 7-8 years from when I first thought "Python can give me a serious productivity boost relative to C" to that point.
I have used Ocaml "for real" and I am still getting up to speed on Haskell (I love it!), but this time, I can skip all the struggle for acceptance. I can just ship 'em a binary. Is the type system going to give me as big a boost in real productivity (incl. debugging and support overheads) as Python did over C? Well we shall see...
What are your reasons for choosing a dynamic language?
And my answer was, to get a productivity boost over C. At the time, there was no strongly-typed language that it was feasible to use that offered as much leverage. Now there is, and one of the reasons for that is the new crop of functional type-inferred languages that "play nicely" with existing estates by deploying as compiled code (native, or .NET, or JVM).
Very few people have the luxury of coding in a vacuum. Language choice is very rarely a purely technical one.
Perhaps you meant to ask about "weakly vs strongly" typed?
Static typing can be just as concise as dynamic typing, and sometimes more. For example, Haskell's Maybe monad combined with do-notation lets you write code that has null checks where required without uglifying the code.
The benefits of static type checking haven't really been fully realized in popular languages. Type inference is almost mainstream, but you can go a lot farther with non-nullable types, structural typing, dependent typing, etc.
I'd agree but I think that's because designing a static typing system rigid enough to be useful but flexible enough to be expressive is a lot harder than building a dynamic language. That doesn't mean though that there's nothing to be gained from that hard work. It took a long time to design operating systems that allowed truly robust, compartmentalized multitasking too, but they clearly turned out to be superior for most things in the end.
My instinct is that we're going to see a return of static typing at the cutting edge of production coding but I'm not sure if this current crop of options is going to effect this. Scala seems to me to be the only one with a shot at the mainstream for this round.
Nowadays I use Haskell for the same reasons, and Haskell is as statically typed as they come.
The one single thing that really made dynamic typing obsolete for me was type inference.
Other languages, like Ruby or JavaScript, attract people who shorten document.querySelector(id) to jQuery(id), come up with things like Processing (JVM but short syntax school), CoffeeScript, Rails, NodeJS, etc..
AS 3.0 is an interesting example. It's the language of the Flash plugin, which used to be popular among non-programmers as it allowed them to hack things together concisely, without having to learn about types or classes, since old AS 2.0 looked like JS. This was an audience the makers of Flash wanted to keep, but when AS 3.0 came out it chased them away. Online examples became polluted with design patterns, commands, factories, optimizations, complex best practices, long names. Libraries were targeted at advanced coders, there was nothing like jQuery for AS 3.0. When non-programmers see obj.addEventListener(MouseEvent.CLICK, clickHandler) they start shaking. JS frameworks do obj.on("click", onClick) or obj.click(onClick), few irrelevant details.
The language attracted serious developers who were used to big projects. Adobe released Flex, a gigantic framework reminiscent of the Java world, 300-600KB zipped for web deployment. Years later efforts like http://www.hypeframework.org/ tried to rekindle some of the old flame but the non-programmers are still using AS 2.0 or switched to JS.
Did AS 3.0 have to be as verbose? Did it need giant frameworks and Java developers? Did it need types? No. The performance improvements from AS 3.0 types were negligible, they can be left out in a non-strict mode and the difference is 2 FPS, maybe. For typical Flash projects, debugging with types isn't necessary. Supposedly the situation improved performance wise for types, but that's the wrong priority.
JS/HTML5 picked the smarter approach. There will be typed arrays for WebGL, they're ugly, which discourages their use unless actually necessary. Type strict mode applied to an entire app means example and open source code will have it, it pushes everyone to use it.
When subtle psychological differences are applied to large groups, they create obvious effects. The current division between typed verbose and dynamic concise emerged from social forces more than technical issues. There are efforts like Google's Go, typed Ruby, dynamic C# 4.0, typed arrays in JS, but they're all recent and it will take time for developers to discover new preferences.
> those who code C++, Java and ActionScript 3.0 just love piling on
> boilerplate code and long names like FactoryBuilderFacadeInterface
boilerplate code is not caused by the language, but by the programmer,
I know more than few examples of compact/clean/well-named code in C++, Java, AS3, etc. > since old AS 2.0 looked like JS
no, that was AS 1.0
AS 2.0 was statically checked at compilation time > [...] but the non-programmers are still using AS 2.0 or switched to JS.
it's not a problem of being a non-programmer vs being a programmer,
it's a problem of a group of people learning a certain way to program with AS 2.0,
and when an update occurs to the language are too lazy to change their habits > Did AS 3.0 have to be as verbose? Did it need giant frameworks and
> Java developers? Did it need types? No.
You're mixing a bit of everything there
AS 3.0 is not verbose, you can still compile it "AS 1.0 style", just use
the compiler MXMLC -ES and yes by doing that nothing is statically checked
and all built-in classes default to "prototype" mode.
(for ex: http://hg.mozilla.org/tamarin-central/file/fbecf6c8a86f/core...)For the giant framework part, yes the Flex framework is big, but it does not define what I would call an official AS 3.0 framework, in fact AS 3.0 at the language level is pretty compact, the "real" framework is the Flash Platform API (or if you prefer the native classes available in the Flash Player or AIR).
So, again a problem of understanding with types, let be clear about something here, you seem to think that because JavaScript or AS 1.0 does not need to assign types to variables and because they are not statically type checked at compilation or interpretation, that there is no types, sorry they have types.
Read those articles from Eric Lippert The JScript Type System, Part One http://blogs.msdn.com/b/ericlippert/archive/2003/11/05/53336...
"JScript Classic is a weakly typed language. This means that any value of any type may be assigned to any variable without restriction. It is often said -- inaccurately -- that "JScript has only one type". This is true only in the sense that JScript has no restrictions on what data may be assigned to any variable, and in that sense every variable is "the same type" – namely, the "any possible value" type. However, the statement is misleading because it implies that JScript supports no types at all, when in fact it supports six built-in types. The "weak" in "weakly typed" refers to the weak restrictions on variables, not on the type system per se."
There are 8 parts of "The JScript Type System" and mho it is a must read if your language of choice is based on ECMAScript.
> The performance improvements from AS 3.0 types were negligible, they can be left out in
> a non-strict mode and the difference is 2 FPS, maybe. For typical Flash projects,
> debugging with types isn't necessary. Supposedly the situation improved performance
> wise for types, but that's the wrong priority.
negligible ?OK, first thing first, when you compile AS 1.0/2.0 code, the generated bytecode (in the SWF) is run by the AVM1 (ActionScript Virtual Machine).
The Flash Player support 2 VM: AVM1 and AVM2, AVM2 is the Tamarin VM (http://www.mozilla.org/projects/tamarin/).
So when you compile AS 3.0 code, the generated bytecode is run by the AVM2, which support runtime type annotations and is much much much faster because of that.
But let's see hard data
http://www.masonchang.com/blog/2008/4/28/tamarin-benchmarks-...
http://www.masonchang.com/blog/2008/4/28/tamarin-benchmarks-...
see the difference with and without type annotation ?
now if you want all the greedy details of why this AVM2 is much faster you can look at http://www.adobe.com/devnet/actionscript/articles/avm2overvi...
That's right, I said the language attracts a certain type of programmer, who codes with boilerplate. There are quick and dirty AS 3.0 libraries, but there is an abundance of frameworks that help you organize code like PureMVC and Robotlegs which add a healthy amount of complexity. JS world has a higher proportion of simple libraries like JQuery than big and heavy ones like Google's Closure, a visitor from Java land.
Flex framework is what Adobe thought would help developers. They spent some effort promoting it and integrating it into their IDE. This means they're not focused on graphics heavy projects that Flash is famous for, but are expanding into the big utilitarian app territory. Such moves attract verbose programmers, who write complex example code etc..
The standard API in AS 3.0 is also more verbose than the AS 1.0, 2.0 or canvas equivalents. This gives advanced programmers better control, while neglecting the needs of novice coders. Adobe is pretty worried about the situation actually, very few novice coders bother with AS 3.0 because they judge syntax visually and AS 3.0 looks longer.
"you can still compile it "AS 1.0 style", just use the compiler MXMLC -ES"
I mentioned that online examples and libraries would use types, which pushes everyone to use them. Using non-strict mode when everyone is using strict is less convenient than just using strict.
I don't know what VM non-strict mode in AS3.0 uses, I thought it uses AVM2, but I shouldn't need to know. I've tried 2D AS 3.0 physics simulations with types and without and the difference was difficult to measure. Most of the time was spent on rendering visuals, not on calculations, so there was no real world speed advantage in that situation. The cases where there would be, like encoding images, need threading anyway.
For a language that isn't a speed demon in the first place, it makes no sense to worry about silly optimizations that are noticeable only in unrealistic benchmarks. Knowing that you need to use int instead of uint to access indexed arrays slightly faster is distracting.
"it's not a problem of being a non-programmer vs being a programmer, it's a problem of a group of people learning a certain way to program with AS 2.0, and when an update occurs to the language are too lazy to change their habits"
I picked up programming a few years ago so I still remember how painful it was to switch languages when I was a novice. Now that I'm not a novice, switching to similar languages is barely noticeable because I understand the fundamentals. Keep in mind, lots of these novices code a simple thing here and there, they don't write large programs so they don't really learn programming. When things change drastically, it makes more sense for these people to stick to AS 2.0 if they can or let a developer handle it.
This is not so much a problem for novices, because they have options, this is a problem for Adobe since novices have options and are choosing Processing and HTML5.
"let be clear about something here, you seem to think that because JavaScript or AS 1.0 does not need to assign types to variables and because they are not statically type checked at compilation or interpretation, that there is no types, sorry they have types."
Yes there are types, but I'm talking about perceived language complexity and boilerplate code psychology. There are few instances where you have to worry about types in JS, but it's a constant time drain in AS 3.0.
My preferred language has dynamic typing, but its common implementations also do type inference at compile time, in order to optimize generic operations, and to warn about things that cannot work. Since during development I only recompile the snippet I am working on, while the rest of the system is running on, the compiler can use all the type information available from the running system.
euh... even if a language is considered dynamically typed and/or using weak types, the language still have types.
Another factor might be the availability of type inferencing, which makes interactive prompts perhaps more viable, as you can for example type "1 + 1" into the prompt, and its type is automatically deduced.
Have you ever tried Prolog? While it has a lot of quirks* , it's an excellent prototyping language. (It's also usually dynamically typed, though some implementations have type annotations.)
Instead of a usual function(arguments) -> result model, it treats everything like a search query, incrementally suggesting every possible variable instances that fit known rules. Since search, backtracking, and unification are built in, a lot of code becomes implicit, and you can prototype things quite rapidly.
* Briefly: I think it would be better as an embedded library (like, say, SQLite or Lua) rather than a standalone language. It's excellent for some things, but IO / side-effects are awkward in its model.
You still need polymorphism for some things, and having the possibility to have "implicit interfaces" (you just write x.foo() without declaring that x implements the foo interface, or that y and z also implement it - this is also useful when y or z are library classes you cannot change).
Often, the type inference that is needed slows down the compile times quite a bit; and (both with Haskell and with C++) you get incomprehensible error messages when something is wrong but it could have gone wrong in half a dozen places. (C++: 6 lines with "in ...", then "no such method: foo", Haskell: expected type bla->foo->Z x, found a->B c->d)
IMO, static type analysis on top of a dynamic language (e.g. PyDev) gives you most of the benefits of IDE and refactoring support - with the additional coolness that it can just skip the places that are not type-able. (Optional type declarations to put PyDev back on track in the most obvious cases would be great, though).
I started using type specs and dialyzer - it finds typing errors, and make building complex systems in Erlang much easier. The only problem that dialyzer requires intermediate step of building PLT, which sometimes can takes 20 minutes and more, but only once per project.
Sometimes the type system won't type check a perfectly valid program. One thing that I bump into with nearly every program I write in C# or F# is that I want to add a method to a generic type when the type supports some method. Suppose you have a type Bag<T>. Now if T supports an interface IValue with method ComputeValue() I want to add a ComputeValue method to Bag<T> that computes the sum of the values of the things in the Bag. Of course the method would only be available when T supports IValue. This can be done in languages with more expressive type systems, but there are (provably) other perfectly valid programs that they will reject.
Another reason against statically typed languages is that the compilers are often slow. Compile+Run takes several seconds (if you're lucky).
It's also fast enough that I stopped caring years ago. Don't get me wrong, Eclipse is agonizing when it locks up while "helping" me, but it's not due to compiling (it happens just as often when I haven't made any changes).
It gets really irritating when it's an attribute of an object that's returned by a getter that's wrong and cannot be cast, then I need to figure out if this thing is used anywhere other then the place I'm at and if it is, create a new method or... at this point the code is just getting in my way.
Statically typed languages tend to lack good REPLs, a tool I consider indispensable. I'm unaware of any statically typed language that has something as complete and integrated as SLIME, for instance. Even a properly setup IRB or IPython don't seem to have anything that matches them in the statically typed world.
Another problem is that statically typed languages can have long compile cycles. One C# project I worked on made heavy use of PostSharp, the use of which slowed the compile time to nearly a minute. Additionally, whilst Visual Studio updated the ASP templates in real time, there didn't seem to be a way to reload new DLLs without restarting the development server.
Conversely, when I'm hacking in Clojure, updating the entire system can be done without restarting the server, and takes a fraction of a second.
Macros, too, I guess are easier with dynamic typing. When I write a macro in Clojure, it seems a lot easier than when I was using Template Haskell a few years back. Perhaps there's a better system for macros in a statically typed language, but I haven't run across anything like that yet.
That only applies if you compile all classes at once. Netbeans (and probably others as well) compile Java classes instantly because they only compile what's changed.
there didn't seem to be a way to reload new DLLs without restarting the development server.
I have seen environments for Java that could reload JAR-files. Perhaps DLLs are different, but that has little to do with a statically typed language.
Unfortunately, a lot of post-processors like PostSharp aren't smart enough to do that. Even though Visual Studio was only compiling once, PostSharp seemed to require a lot of time to do its thing.
I have seen environments for Java that could reload JAR-files. Perhaps DLLs are different, but that has little to do with a statically typed language.
I'd be interested if anyone knows of an environment for a statically typed language that could do this quickly and without losing run-time data.
In a dynamically typed language, altering the server environment at runtime incurs only the cost of evaluating the code. I can inject code at any point, redefine functions, query the state of the current data structures, and so forth. The entire environment is mutable; something that seems like it would be difficult to do with static types.
statically typed: is-a, has-a
dynamically typed: is-like-a, has-like-a
Read the introduction from
Dynamic Typing in a Statically Typed Language
http://lucacardelli.name/Papers/Dyn.ps
"Even in a statically typed language there is often the need to deal with data
whose type cannot be determined at compile time"much more other interesting papers http://lucacardelli.name/indexPapers.html#Types and Semantics
If you find that today's C# can be more concise, it's simply because since C# 1.0 to C# 4.0 they added a lot of extensions that can make the language more dynamic (see http://en.wikipedia.org/wiki/C_Sharp_4.0 with the use of 'dynamic').
In comparaison, if you look at the ActionScript language which have its roots based on ECMA-262(ECMAScript), from AS 1.0 to AS 3.0 they evolved the language to make it statically typed, but you still keep dynamic functionalities like the * (any) type or the option to declare a class dynamic and add members to the prototype.
After it can be a long debate to try to prove which one is "better".
For example, combine unit tests and a dynamic language and you don;t really feel you're missing anything with type checking. Counter example, you can have a statically type checked language that compile and can still throw errors at runtime.
> And on the plus side, you get all the benefits of compiler type checking, better IDE support, and great refactoring support.
Since when? Compile time type checking is only a benefit if you're worried about type errors and in my experience the people that are most concerned with type errors are the proponents of statically typed languages. With respect to IDEs and refactoring support, my experience has been more along the lines of "C# and Java have good IDEs and refactoring tools, everything else not so much." I personally find Scala and Haskell's tool support to be far behind Python's, just for example. Also, Smalltalk has, arguably, far better tools available than any statically typed language (or any other language for that matter).
Nobody has proven that statically typed languages are better than dynamically typed ones, nor that applications built with them are less error pronen. I would argue that the amazingly widespread use of Ruby and Python and the average quality of projects written in those languages is evidence to the contrary.
def some_function(arg1, arg2) arg1.
The IDE won't show you a list with possible functions because arg1 can be everything.
Projects where people are able to choose the language will probably be of higher quality than projects where they're stuck using Java, C#, etc.
To some extent, this is probably independent of the languages themselves - there are a lot of terrible Java programmers because it's a default language, rather than one sought out by curious and motivated programmers.
I have been programming Python for many years now, along with a mix of C#, Scala and some F#. I find that the programming idioms that I care about are supported in all of them.
There are some valid problems, but IMHO they are not major:
1. Compilation Time (But do we have to compile that often? Although I find myself doing this more often with Python, to make sure I did not make a spelling mistake!)
2. Duck-Typing (Yes good to have. In future we might see more Structural Subtyping)
But what tipped the scale is my experience with refactoring large projects. In dynamically typed languages, it is scary to make a even simple changes in code you haven't touched in a while. Unless you have full test coverage, plus insurance. In statically typed langs, this is much easier.
We run software services company. We have a policy of refactoring suboptimal code on sight. We try to do it in all projects, but I always see worried looks when refactoring dynamically typed code. And not so much when we are modifying, say C#.
On the other hand, I am the complete opposite. I often work for hours or days, sometimes even weeks, on a program without ever running it. This is only possible when 99.9% of all errors immediately caught by the compiler (or IDE). Also, I document everything that I do extensively with Javadocs or similar mechanisms. When I write in dynamic languages, I usually start writing the static type annotations ("//array of string") as comments, without having the benefit of using code completion and refactoring. So effectively, for me, dynamic languages mean more typing rather than less.
def foo(lst):
if type(lst) is str:
#return do_string_stuff_to(lst)
else:
#handle do_list_stuff_to(lst)
And I'm done. There's a good chance I'll throw the function away or re-write it before I ever care about the sort of edge cases that would get caught by a static compiler. If the algorithm works on a dictionary or set just as well as the list I had in mind, so much the better. In the meantime it's doing its work and helping me get other things done.C#'s standard library, for example, has common interfaces for collections so the code will still work on all kinds of lists, arrays, etc.
This is the key difference in attitude. I don't want a language that frictionlessly helps me screw up. If what I'm doing might work but nobody actually knows, I want some kind of warning. At the very least I need to add your code to my test coverage.
if isinstance(lst, str): if isinstance(obj, basestring):
So that it works with unicode in Python >= 2.3I'm not a good programmer so sometimes I have tiny errors and such and being able to vim exec.py fix ./exec.py can help tons.
I do a good amount of things in matlab and must say that I use the cmd line (REPL?) infinitely along with my programs, it is just so easy for me to find solutions that way compared to something like C# and for someone that doesn't always know how a function works using it on the command line with documentation is great.
So there ARE statically typed languages with a REPL, for example Haskell.
And there are dynamically typed languages without a REPL - for example Perl (in the core distribution).
Static typing means that for example, a list can only contain one type of element. And because of these constraints, the compiler can find some errors that are not found otherwise.
Not having used static typed Python I can't say for sure but I believe significant indentation, std lib, and how language generally follows "the zen of python" are at least as if not more important than static typing.