views:

397

answers:

6

Simple question folks: I do a lot of programming (professionally and personally) in compiled languages like C++/Java and in interpreted languages like Python/Javascript. I personally find that my code is almost always more robust when I program in statically typed languages. However, almost every interpreted language I encounter uses dynamic typing (PHP, Perl, Python, etc.). I know why compiled languages use static typing (most of the time), but I can't figure out the aversion to static typing in interpreted language design.

Why the steep disconnect? Is it part of the nature of interpreted languages? OOP?

+4  A: 

Interpreted languages use dynamic typing because there is no compilation step in which to do the static analysis. Compiled languages do static analysis at compilation time which means that any type errors are reported to the developer as they work.

It is easier to understand if you consider that a statically typed language has a compiler that enforces type rules outside the context of execution. Interpreted languages are never analyzed statically so type rules must be enforced by the interpreter within the context of execution.

Andrew Hare
Thanks for your answer, Andrew. I understand this, but I still don't see a reason why interpreted languages can't do this analysis at runtime.
daveslab
Thanks again for the update. However, perhaps I'm not addressing my question properly. Re: the last sentence of your explanation, is there any limitation that prevents interpreted languages from being analyzed due to the inherent nature of interpreted languages? Or is it just a popular language design choice?
daveslab
"Static" means that an operation is performed at compile-time; "dynamic" means it is deferred to runtime. Interpreted languages have no (manual) compilation step. You can and do have dynamically-typed languages which strong typing, though (e.g., Python).
mipadi
Thanks, mipadi. That helps me I think.
daveslab
daveslab: consider a language like Python. There is no static type checking in Python, but the language is *still strongly typed*. This means that each value has a single, well-defined type at runtime. Doing `"foo" + 3` throws a TypeError at runtime. So some languages do the analysis at runtime.
Martin Geisler
A: 

I think static typing makes it easier for compilers and that's the main (if not only) reason that it's present in compiled languages.

For interpreted languages it's easier to assume that variables don't have type (only values have) because they are thought of not as a placer for the data that must fit inside but rather label for the data that floats somewhere on the heap.

If programmer want's he can always assert that variable holds a value of given type (for example on assignment). There is no reason to build it into the language. Of course that not the same kind of control that you have for compiled languages.

You probably could have language in which you have to explicitly declare type of each variable but if you don't it's much easier to do interesting things that with static typing would require from programmer very carefully crafted complex generic types.

On the other hand. Do you know any dynamically typed compiled (statically, not JIT) language?

Kamil Szot
Factor is a compiled language with dynamic typing.
mipadi
A: 

I think it's because of the nature of interpreted languages, they want to be dynamic, so you CAN change things at runtime. Due to this a compiler never exactly knows what's the state of the program after the next line of code has been excecuted.

Imagine the following scenario(in Python):

import random
foo = 1

def doSomeStuffWithFoo():
    global foo
    foo = random.randint(0, 1)

def asign():
    global foo
    if foo == 1:
        return 20
    else:
        return "Test"


def toBeStaticallyAnalyzed():
    myValue = asign()

    # A "Compiler" may throw an error here because foo == 0, but at runtime foo maybe 1, so the compiler would be wrong with its assumption
    myValue += 20


doSomeStuffWithFoo() # Foo could be 1 or 0 now... or 4 ;)
toBeStaticallyAnalyzed()

As you can hopefully see, a compiler wouldn't make any sense in this situation. Acutally it could warn you about the possibility that "myValue" maybe something else than a Number. But then in JavaScript that would fail because if "myValue" is a String, 20 would be implictily converted to a String too, hence no error would occur. So you might get thousands of useless warnings all over the place, and i don't think that that is the intend of a compiler.

Flexibility always comes with a price, you need to take a deeper look at your program, or program it more carefully, in other words you are the COMPILER in situations like the above.

So your solution as the compiler? - Fix it with a "try: except" :)

Ivo Wetzel
Yes, it's pretty clear that if you do some dynamic type stuff in a dynamically typed language the compiler couldn't figure out the type. What's not clear is why should the language be dynamically typed, if it were designed such that each function's return and variable were of a declared type it could then check for the type.
dlamblin
The language is dynamically typed BECAUSE of its design, the design is so that you can change stuff at runtime, that's what's making it so dynamic. If a function would return a typed value, what happens then if you asign that to a untyped variable? More dynamic conversion! So the typing in one half of the language wouldn't make it any better. In the end you have the choice betwenn static typed, more machine checked code and dynamic typed more human check code, which is also more dynamic, because in the end a human is still more intelligent than a compiler.
Ivo Wetzel
I understand what you're saying. If the language had an `int main(){return 0}` or an `int main(){return "a"}` just because the design allows you to declare a type doesn't mean it will be enforced. But it could be enforced if that was part of the design. However either of these cases could be done as a compiled or interpreted language. Nothing says an interpreted language wants to be dynamic, but I agree that if I wrote an interpreted language, I'd rather make it dynamic.
dlamblin
+2  A: 

Maybe it's because one of my main interpreted languages is Perl and one of my compiled languages is Objective-C, but I never strongly felt that there was anything special about compilation vs interpretation that suggested dynamic over static typing.

I think it's clear that both sides are looking at the other and thinking, "There's some advantages to that." It's easier in several applications to get some dynamic type flexibility, while it can be easier to maintain something that is statically typed and enforced.

I disagree with Andrew Hare's explanation though. While a purely interpreted language would have to add in a preprocessing step, and hence not be purely interpreted in order to warn the programmer before execution of static typing errors, it doesn't preclude throwing a type error at run-time as it occurs. So lacking compilation does not mean no static type checking may occur. But since getting a type error at run-time isn't as useful as getting one at compilation, or during a preflight check, I can see how the "advantage" of static typing in that situation can seem to be more of a nuisance, and thus get thrown out in favor of the advantages dynamic typing bring.

If you knew from the outset that you prefer to keep your types static because personally you write better more maintainable code as a result, and you were designing your interpreted language, nothing should stop you from designing the language as a statically typed one.

To quote the interpreted languages wiki article "Theoretically, any language may be compiled or interpreted, so this designation is applied purely because of common implementation practice and not some underlying property of a language."
There's a decent wiki article on just the typing.

dlamblin
"So lacking compilation does not mean no static type checking may occur. But since getting a type error at run-time isn't as useful as getting one at compilation" - If the type error occurs at runtime, then it's a _dynamic_ check, not a _static_ check. But there are dynamic languages that perform type checking at runtime, and will throw type errors -- this is a dynamically-typed language with _strong_ types.
mipadi
@dlamblin Thanks for your detailed answer. This was the answer I was looking for. @mipadi I don't think that's neccesarily true. You can pass around void pointers in C++ and cast them as whatever you like and get a "type" error (not in the strict sense).
daveslab
What's "not necessarily true"? That a check that occurs at runtime is a _dynamic_ check? That _is_ true: it's the definition of "dynamic" (as opposed to "static", which occurs _before_ runtime).
mipadi
If the runtime tells you your type is wrong, it's because the typing is not dynamic and is static. We're not talking about the type *checking* being dynamic or static. That's why I linked the wiki article.
dlamblin
I know i'm late to this, but I always thought the definition of Static was unchanging and Dynamic was changing. Thus, dynamic-ly typed languages allow changing the type (adding and removing methods, etc) at runtime, and static-ly typed languages don't. Nothing to do with when the check occurs, I see no reason that you couldn't do a static check at runtime (yes I realize I'm late to this)
James Deville
+5  A: 

Interesting question. BTW, I'm the author/maintainer of phc (compiler for PHP), and am doing my PhD on compilers for dynamic languages, so I hope I can offer some insights.

I think there is a mistaken assumption here. The authors of PHP, Perl, Python, Ruby, Lua, etc didn't design "interpreted languages", they designed dynamic languages, and implemented them using interpreters. They did this because interpreters are much much easier to write than compilers.

Java's first implementation was interpreted, and it is a statically typed language. Interpreters do exist for static languages: Haskell and OCaml both have interpreters, and there used to be a popular interpreter for C, but that was a long time ago. They are popular because they allow a REPL, which can make development easier.

That said, there is an aversion to static typing in the dynamic language community, as you'd expect. They believe that the static type systems provided by C, C++ and Java are verbose, and not worth the effort. I think I agree with this to a certain extent. Programming in Python is far more fun than C++.

To address the points of others:

  • dlamblin says: "I never strongly felt that there was anything special about compilation vs interpretation that suggested dynamic over static typing." Well, you're very wrong there. Compilation of dynamic languages is very difficult. There is mostly the eval statement to consider, which is used extensively in Javascript and Ruby. phc compiles PHP ahead-of-time, but we still need a run-time interpreter to handle evals. eval also can't be analysed statically in an optimizing compiler, though there is a cool technique if you don't need soundness.

  • To damblin's response to Andrew Hare: you could of course perform static analysis in an interpreter, and find errors before run-time, which is exactly what Haskell's ghci does. I expect that the style of interpreter used in functional languages requires this. dlamblin is of course right to say that the analysis is not part of interpretation.

  • Andrew Hare's answer is predicated on the questioners wrong assumption, and similarly has things the wrong way around. However, he raises an interesting question: "how hard is static analysis of dynamic languages?". Very very hard. Basically, you'll get a PhD for describing how it works, which is exactly what I'm doing. Also see the previous point.

  • The most correct answer so far is that of Ivo Wetzel. However, the points he describes can be handled at run-time in a compiler, and many compilers exist for Lisp and Scheme that have this type of dynamic binding. But, yes, its tricky.

Paul Biggar
A very thoughtful, well done answer. Thanks very much. There's a lot of sloppy vocab (mine included) around this subject, it seems.
daveslab
A: 

Compilers + Static types = efficient machine code
Compilers + Dynamic types = inefficient machine code

Consider the following pseudocode:

function foo(a, b) {
    return a+b
}

A Static language will be able to know (by declaration or inference) that a and b are integers, and will compile down to

%reg = addi a,b

or something similar, anyway.

A compiler for a dynamic language would have to emit code to
1. Check they types of a and b
2. handle each case or combination of cases

%reg1 = typeof a
beq %reg1, int, a_int_case
beq %reg1, float, a_float_case
beq %reg1, string, a_string_case

label a_int_case
%reg1 = typeof b
beq %reg1, int, a_int_b_int_case
beq %reg1, float, a_int_b_float_case
beq %reg1, string, a_int_b_string_case

label a_int_b_int_case
%out = addi a,b
goto done

label a_int_b_float_case
%tmp = mkfloat a
%out = addf %tmp,b
goto done

... Etc. I can't finish

While you could generate smarter machine code than that, you wouldn't be able to help generating lots of code -- That makes compilation not a major win for a dynamic language.

Since interpreters are much easier to write, and compilation doesn't do you much good, why not write an interpreter?

(Just-in-time compilers actually have type information, and can compile right down to the single statement. They actually have more information than static type systems, and can theoretically do even better. All assembler is simulated; Any resemblance to real code that could run on a real machine is purely coincidental.)

Sean McMillan
I think it would be better to say that `interpreter + dynamic language = inefficient programs`. Compiling dynamic languages is typically efficient, when its possible.
Paul Biggar
@Paul: Sure, interpreters are inefficient, but we all knew that. My point was that naively compiling an interpreted language will produce a program that's not much better than interpreting; Compiling dynamic languages is not "typically" efficient -- making it efficient takes a lot of work, so is not typically done.
Sean McMillan