Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

First I want to say Java is the only language I ever used, so please excuse my ignorance on this subject.

Dynamically typed languages allow you to put any value in any variable. So for example you could write the following function (psuedocode):

void makeItBark(dog){
    dog.bark();
}

And you can pass inside it whatever value. As long as the value has a bark() method, the code will run. Otherwise, a runtime exception or something similar is thrown. (Please correct me if I'm wrong about this).

Seemingly, this gives you flexibility.

However, I did some reading on dynamic languages, and what people say is that when designing or writing code in a dynamic language, you think about types and take them into account, just as much as you would in a statically typed language.

So for example when writing the makeItBark() function, you intent for it to only accept 'things that can bark', and you still need to make sure you only pass these kinds of things into it. The only difference is that now the compiler won't tell you when you made a mistake.

Sure, there is one advantage to this approach which is that in static languages, to achieve the 'this function accepts anything that can bark', you'd need to implement an explicit Barker interface. Still, this seems like a minor advantage.

Am I missing something? What am I actually gaining by using a dynamically typed language?

share|improve this question

closed as primarily opinion-based by Euphoric, MichaelT, GlenH7, Dynamic, Robert Harvey Jul 9 at 22:06

Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise.If this question can be reworded to fit the rules in the help center, please edit the question.

6  
makeItBark(collections.namedtuple("Dog", "bark")(lambda x: "woof woof")). That argument isn't even a class, it's an anonymous named tuple. Duck typing ("if it quacks like a...") lets you do ad hoc interfaces with essentially zero restrictions and no syntactic overhead. You can do this in a language like Java, but you end up with a lot of messy reflection. If a function in Java requires an ArrayList and you want to give it another collection type, you're SOL. In python that can't even come up. –  Phoshi Jul 3 at 9:54
2  
This kind of question has been asked before: here, here, and here. Specifically the first example seems to answer your question. Maybe you can rephrase yours to make it distinct? –  logc Jul 3 at 9:55
3  
Note that for example in C++, you can have a template function that works with any type T that has a bark() method, with the compiler complaining when you pass in something wrong but without having to actually declare an interface that contains bark(). –  Wilbert Jul 3 at 9:56
2  
@Phoshi The argument in Python still has to be of a particular type - for example, it can't be a number. If you have your own ad-hoc implementation of objects, which retrieves its members through some custom getMember function, makeItBark blows up because you called dog.bark instead of dog.getMember("bark"). What makes the code work is that everyone implicitly agrees to use Python's native object type. –  Doval Jul 3 at 13:30
2  
@Phoshi Just because I wrote makeItBark with my own types in mind doesn't mean you can't use yours, wheras in a static language it probably /does/ mean that. As pointed out in my answer, this is not the case in general. That's the case for Java and C#, but those languages have crippled type and module systems so they're not representative of what static typing can do. I can write a perfectly generic makeItBark in several statically-typed languages, even non-functional ones like C++ or D. –  Doval Jul 3 at 13:50

3 Answers 3

up vote 29 down vote accepted

Dynamically-typed languages are uni-typed

Comparing type systems, there's no advantage in dynamic typing. Dynamic typing is a special case of static typing - it's a statically-typed language where every variable has the same type. You could achieve the same thing in Java (minus conciseness) by making every variable be of type Object, and having "object" values be of type Map<String, Object>:

void makeItBark(Object dog) {
    Map<String, Object> dogMap = (Map<String, Object>) dog;
    Runnable bark = (Runnable) dogMap.get("bark");
    bark.run();
}

So, even without reflection, you can achieve the same effect in just about any statically-typed language, syntactic convenience aside. You're not getting any additional expressive power; on the contrary, you have less expressive power because in a dynamically typed language, you're denied the ability to restrict variables to certain types.

Making a duck bark in a statically-typed language

Moreover, a good statically-typed language will allow you to write code that works with any type that has a bark operation. In Haskell, this is a type class:

class Barkable a where
    bark :: a -> unit

This expresses the constraint that for some type a to be considered Barkable, there must exist a bark function that takes a value of that type and returns nothing.

You can then write generic functions in terms of the Barkable constraint:

makeItBark :: Barkable a => a -> unit
makeItBark barker = bark (barker)

This says that makeItBark will work for any type satisfying Barkable's requirements. This might seem similar to an interface in Java or C# but it has one big advantage - types don't have to specify up front which type classes they satisfy. I can say that type Duck is Barkable at any time, even if Duck is a third party type I didn't write. In fact, it doesn't matter that the writer of Duck didn't write a bark function - I can provide it after-the-fact when I tell the language that Duck satisfies Barkable:

instance Barkable Duck where
    bark d = quack (punch (d))

makeItBark (aDuck)

This says that Ducks can bark, and their bark function is implemented by punching the duck before making it quack. With that out of the way, we can call makeItBark on ducks.

Standard ML and OCaml are even more flexible in that you can satisfy the same type class in more than one way. In these languages I can say that integers can be ordered using the conventional ordering and then turn around and say they're also orderable by divisibility (e.g. 10 > 5 because 10 is divisible by 5). In Haskell you can only instantiate a type class once. (This allows Haskell to automatically know that it's ok to call bark on a duck; in SML or OCaml you have to be explicit about which bark function you want, because there might be more than one.)

Conciseness

Of course, there's syntactical differences. The Python code you presented is far more concise than the Java equivalent I wrote. In practice, that conciseness is a big part of the allure of dynamically-typed languages. But type inference allows you to write code that's just as concise in statically-typed languages, by relieving you of having to explicitly write the types of every variable. A statically-typed language can also provide native support for dynamic typing, removing the verbosity of all the casting and map manipulations (e.g. C#'s dynamic).

Correct but ill-typed programs

To be fair, static typing necessarily rules out some programs that are technically correct even though the type checker can't verify it. For example:

if this_variable_is_always_true:
    return "some string"
else:
    return 6

Most statically-typed languages would reject this if statement, even though the else branch will never occur. In practice it seems no one makes use of this type of code - anything too clever for the type checker will probably make future maintainers of your code curse you and your next of kin. Case in point, someone successfully translated 4 open source Python projects into Haskell which means they weren't doing anything that a good statically-typed language couldn't compile. What's more, the compiler found a couple of type-related bugs that the unit tests weren't catching.

The strongest argument I've seen for dynamic typing is Lisp's macros, since they allow you to arbitrarily extend the language's syntax. However, Typed Racket is a statically-typed dialect of Lisp that has macros, so it seems static typing and macros are not mutually exclusive, though perhaps harder to implement simultaneously.

Apples and Oranges

Finally, don't forget that there's bigger differences in languages than just their type system. Prior to Java 8, doing any kind of functional programming in Java was practically impossible; a simple lambda would require 4 lines of boilerplate anonymous class code. Java also has no support for collection literals (e.g. [1, 2, 3]). There can also be differences in the quality and availability of tooling (IDEs, debuggers), libraries, and community support. When someone claimed to be more productive in Python or Ruby than Java, that feature disparity needs to be taken into account. There's a difference between comparing languages with all batteries included, language cores and type systems.

share|improve this answer
1  
You forgot to attribute your source for the first paragraph -- existentialtype.wordpress.com/2011/03/19/… –  user39685 Jul 3 at 13:51
2  
@Matt Re: 1, I haven't assumed it's not important; I addressed it under Conciseness. Re: 2, although I never explicitly said it, by "good" I mean "has thorough type inference" and "has a module system that allows you to match code to type signatures after the fact", not up-front like Java/C#'s interfaces. Re 3, the burden of proof is on you to explain to me how given two languages with equivalent syntax and features, one dynamically-typed and the other with full type inference, you wouldn't be able to write code of equal length in both. –  Doval Jul 3 at 14:11
3  
@MattFenwick I've already justified it - given two languages with the same features, one dynamically-typed and the other statically-typed, the main difference between them will be the presence of type annotations, and type inference takes that away. Any other differences in syntax are superficial, and any differences in features turns the comparison into apples vs oranges. It's on you to show how this logic is wrong. –  Doval Jul 3 at 14:29
1  
You should have a look at Boo. It's statically typed with type inference, and has macros that allow for the language's syntax to be extended. –  Mason Wheeler Jul 3 at 14:36
1  
@Doval: True. BTW, lambda notation is not used exclusively in functional programming: as far as I know, Smalltalk has anonymous blocks, and Smalltalk is as object-oriented as it can get. So, often the solution is to pass around an anonymous block of code with some parameters, no matter if this is an anonymous function or an anonymous object with exactly one anonymous method. I think these two constructs express essentially the same idea from two different perspectives (the functional and the object-oriented one). –  Giorgio Jul 3 at 18:48

This is a difficult, and quite subjective issue. (And your question may get closed as opinion-based, but that doesn't mean it's a bad question - on the contrary, even thinking about such meta-language questions is a good sign - it's just not well-suited to the Q&A format of this forum.)

Here's my view of it: The point of high-level languages is to restrict what a programmer can do with the computer. This is surprising to many people, since they believe the purpose is to give users more power and achieve more. But since everything you write in Prolog, C++ or List is eventually executed as machine code, it is actually impossible to give the programmer more power than assembly language already provides.

The point of a high-level language is to help the programmer to understand the code they themselves have created better, and to make them more efficient at doing the same thing. A subroutine name is easier to remember than a hexadecimal address. An automatic argument counter is easier to use than a call sequence here you have to get the number of arguments exactly right on your own, with no help. A type system goes further and restricts the kind of arguments you can provide in a given place.

Here is where people's perception differs. Some people (I'm among them) think that as long as your password checking routine is going to expect exactly two arguments anyway, and always a string followed by a numeric id, it's useful to declare this in the code and be automatically reminded if you later forget to follow that rule. Outsourcing such small-scale book-keeping to the compiler helps free your mind for higher-level concerns and makes you better at designing and architecting your system. Therefore, type systems are a net win: they let the computer do what it's good at, and humans do what they're good at.

Others see to quite differently. They dislike being told by a compiler what to do. They dislike the extra up-front effort to decide on the type declaration and to type it. They prefer an exploratory programming style where you write actual business code without having a plan that would tell you exactly which types and arguments to use where. And for the style of programming they use, that may be quite true.

I'm oversimplifying dreadfully here, of course. Type checking is not strictly tied to explicit type declarations; there is also type inference. Programming with routines that actually do take arguments of varying types does allow quite different and very powerful things that would otherwise be impossible, it's just that a lot of people aren't attentive and consistent enough to use such leeway successfully.

In the end, the fact that such different languages are both very popular and show no signs of dying off shows you that people go about programming very differently. I think that programming language features are largely about human factors - what supports the human decision-making process better - and as long as people work very differently, the market will provide very different solutions simultaneously.

share|improve this answer
3  
Thanks for the answer. You said that some people ' dislike being told by a compiler what to do. [..] They prefer an exploratory programming style where you write actual business code without having a plan that would tell you exactly which types and arguments to use where.' This is the thing that I don't understand: programming isn't like musical improvisation. In music if you hit a wrong note, it may sound cool. In programming, if you pass something into a function that isn't supposed to be there, you'll most likely get nasty bugs. (continuing in next comment). –  Aviv Cohn Jul 3 at 10:06
3  
I agree, but many people don't agree. And people are quite possessive about their mental preconceptions, particularly since they're often unaware of them. That's why debates about programming style usually degenerate into arguments or fights, and it's rarely useful to start them with random strangers on the internet. –  Kilian Foth Jul 3 at 10:09
1  
This is why - judging by what I read - people using dynamic languages take types into account just as much people using static languages. Because when you write a function, it's supposed to take arguments of a specific kind. Doesn't matter if the compiler enforces this or not. So it comes down to static typing helping you with this, and dynamic typing doesn't. In both cases, a function has to take a specific kind of input. So I don't see what the advantage of dynamic typing is. Even if you prefer an 'exploratory programming style', you still can't pass whatever you want into a function. –  Aviv Cohn Jul 3 at 10:09
1  
People often talk about very different types of projects (especially regarding size). The business logic for a web site will be very simple compared to say a full ERP system. There is less risk that you get things wrong and the advantage of being able to very simply reuse some code is more relevant. Say I have some code that generates a Pdf (or some HTML) from a data structure. Now I have a different data source (first was JSON from some REST API, now it's Excel importer). In a language like Ruby it can be super easy to 'simulate' the first structure, 'make it bark' and reuse the Pdf code. –  thorsten müller Jul 3 at 10:40
    
@Prog: The real advantage of dynamic languages is when it comes to describing things which is really hard with a static type system. A function in python, for example, could be a function reference, a lambda, a function object, or god knows what and it'll all work the same. You can build an object that wraps another object and automatically dispatches methods with zero syntactic overhead, and every function essentially magically has parametrized types. Dynamic languages are amazing for quickly getting stuff done. –  Phoshi Jul 3 at 12:06

Code written using dynamic languages is not coupled to a static type system. Therefore, this lack of coupling is an advantage compared to poor/inadequate static type systems (although it may be a wash or a disadvantage compared to a great static type system).

Furthermore, for a dynamic language, a static type system doesn't have to be designed, implemented, tested, and maintained. This could make the implementation simpler compared to a language with a static type system.

share|improve this answer
1  
Don't people tend to eventually re-implement a basic static type system with their unit tests (when targeting a good test coverage)? –  Den Jul 7 at 15:28
    
Also what do you mean by "coupling" here? How would it manifest in an e.g. micro-services architecture? –  Den Jul 7 at 15:28
    
@Den 1) good question, however, I feel that it's outside the scope of the OP and of my answer. 2) I mean coupling in this sense; briefly, different type systems impose different (incompatible) constraints on code written in that language. Sorry, I can't answer the last question -- I don't understand what's special about micro-services in this regard. –  user39685 Jul 7 at 16:57
1  
@Den: Very good point: I often observe that unit tests I write in Python cover errors that would be caught by a compiler in a statically typed language. –  Giorgio Jul 7 at 17:09
    
@MattFenwick: You wrote that it is an advantage that "... for a dynamic language, a static type system doesn't have to be designed, implemented, tested, and maintained." and Den observed that you often do have to design and test your types directly in your code. So the effort is not removed but moved from language design to the application code. –  Giorgio Jul 7 at 17:30

Not the answer you're looking for? Browse other questions tagged or ask your own question.