What functionality does dynamic typing allow?

up vote 83 down vote favorite
27

I've been using python for a few days now and I think I understand the difference between dynamic and static typing. What I don't understand is under what circumstances it would be preferred. It is flexible and readable, but at the expense of more runtime checks and additional required unit testing.

Aside from non-functional criteria like flexibility and readability, what reasons are there to choose dynamic typing? What can I do with dynamic typing that isn't possible otherwise? What specific code example can you think of that illustrates a concrete advantage of dynamic typing?

dynamic-typing static-typing
share|improve this question
edited Oct 5 at 19:22

community wiki

4 revs, 3 users 57%
Justin984
5  
Theoretically there's nothing you can't do in either, as long as the languages are Turing Complete. The more interesting question to me is what's easy or natural in one vs. the other. There are things I do regularly in Python that I wouldn't even consider in C++ even though I know it's capable. – Mark Ransom Oct 3 at 21:19
26  
As Chris Smith writes in his excellent essay What to know before debating type systems: "The problem, in this case, is that most programmers have limited experience, and haven't tried a lot of languages. For context, here, six or seven doesn't count as "a lot." ... Two interesting consequences of this are: (1) Many programmers have used very poor statically typed languages. (2) Many programmers have used dynamically typed languages very poorly." – Daniel Pryden Oct 3 at 21:38
3  
@suslik: If language primitives have nonsensical types, then of course you can do nonsensical things with types. That has nothing to do with the difference between static and dynamic typing. – Jon Purdy Oct 4 at 4:36
8  
@CzarekTomczak: That is a feature of some dynamically-typed languages, yes. But it is possible for a statically-typed language to be modifiable at runtime. For example, Visual Studio allows you to rewrite C# code while you're at a breakpoint in the debugger, and even rewind the instruction pointer to re-run your code with new changes. As I quoted Chris Smith in my other comment: "Many programmers have used very poor statically typed languages" -- don't judge all statically typed languages by the ones you know. – Daniel Pryden Oct 4 at 15:33
9  
@WarrenP: You assert that "dynamic type systems reduce the amount of extra cruft I have to type in" -- but then you compare Python to C++. That isn't a fair comparison: of course C++ is more verbose than Python, but that's not because of the difference in their type systems, it's because of the difference in their grammars. If you just want to reduce the number of characters in your program source, learn J or APL: I guarantee they'll be shorter. A more fair comparison would be to compare Python to Haskell. (For the record: I love Python and prefer it over C++, but I like Haskell even more.) – Daniel Pryden Oct 4 at 15:38
show 11 more comments
feedback

We're looking for long answers that provide some explanation and context. Don't just give a one-line answer: please explain why you're recommending it as a solution. Answers that don't explain anything will be deleted. See Good Subjective, Bad Subjective for more information.

migrated from stackoverflow.com Oct 3 at 20:05

16 Answers

active oldest votes
up vote 45 down vote

Since you asked for a specific example, I'll give you one.

Rob Conery's Massive ORM is 400 lines of code. It's that small because Rob is able to map SQL tables and provide object results without requiring a lot of static types to mirror the SQL tables. This is accomplished by using the dynamic data type in C#. Rob's web page describes this process in detail, but it seems clear that, in this particular use case, the dynamic typing is in large part responsible for the brevity of the code.

Compare with Sam Saffron's Dapper, which uses static types; the SQLMapper class alone is 3000 lines of code.

Note that the usual disclaimers apply, and your mileage may vary; Dapper has different goals than Massive does. I just point this out as an example of something that you can do in 400 lines of code that probably wouldn't be possible without dynamic typing.


Dynamic typing allows you to defer your type decisions to runtime. That's all.

Whether you use a dynamically-typed language or a statically-typed one, your type choices must still be sensible. You're not going to add two strings together and expect a numeric answer unless the strings contain numeric data, and if they do not, you're going to get unexpected results. A statically typed language will not let you do this in the first place.

Proponents of statically type languages point out that the compiler can do a substantial amount of "sanity checking" your code at compile time, before a single line executes. This is a Good Thing™.

C# has the dynamic keyword, which allows you to defer the type decision to runtime without losing the benefits of static type safety in the rest of your code. Type inference (var) eliminates much of the pain of writing in a statically-typed language by removing the need to always explicitly declare types.


Dynamic languages do seem to favor a more interactive, immediate approach to programming. Nobody expects you to have to write a class and go through a compile cycle to type out a bit of Lisp code and watch it execute. Yet that's exactly what I'm expected to do in C#.

share|improve this answer
edited Oct 10 at 14:57

community wiki

8 revs
Robert Harvey
18  
If I added two numeric strings together, I still wouldn't expect a numeric result. – pdr Oct 3 at 20:40
20  
@Robert I agree with most of your answer. However, note that there are statically-typed languages with interactive read-eval-print loops, such as Scala and Haskell. It may be that C# just isn't a particularly interactive language. – Andres F. Oct 3 at 20:57
11  
Gotta learn me a Haskell. – Robert Harvey Oct 3 at 21:25
7  
@RobertHarvey: You might be surprised/impressed with F# if you haven't already tried it. You get all of the (compile-time) type safety that you normally get in a .NET language, except that you rarely ever have to declare any types. The type inference in F# goes beyond what's available/works in C#. Also: similar to what Andres and Daniel are pointing out, F# interactive is part of Visual Studio... – SnOrfus Oct 3 at 21:40
5  
"You're not going to add two strings together and expect a numeric answer unless the strings contain numeric data, and if they do not, you're going to get unexpected results" sorry, this has nothing to do with dynamic vs static typing, this is strong vs weak typing. – vartec Oct 5 at 12:50
show 10 more comments
feedback
up vote 22 down vote

Phrases like "static typing" and "dynamic typing" are thrown around a lot, and people tend to use subtly different definitions, so let's start by clarifying what we mean.

Consider a language that has static types that are checked at compile-time. But say that a type error generates only a non-fatal warning, and at runtime, everything is duck-typed. These static types are only for the programmer's convenience, and do not affect the codegen. This illustrates that static typing does not by itself impose any limitations, and is not mutually exclusive with dynamic typing. (Objective-C is a lot like this.)

But most static type systems do not behave this way. There's two common properties of static type systems that can impose limitations:

The compiler may reject a program that contains a static type error.

This is a limitation because many type safe programs necessarily contain a static type error.

For example, I have a Python script that needs to run as both Python 2 and Python 3. Some functions changed their parameter types between Python 2 and 3, so I have code like this:

if sys.version_info[0] == 2:
    wfile.write(txt)
else:
    wfile.write(bytes(txt, 'utf-8'))

A Python 2 static type checker would reject the Python 3 code (and vice versa), even though it would never be executed. My type safe program contains a static type error.

As another example, consider a Mac program that wants to run on OS X 10.6, but take advantage of new features in 10.7. The 10.7 methods may or may not exist at runtime, and it's on me, the programmer, to detect them. A static type checker is forced to either reject my program to ensure type safety, or accept the program, along with the possibility of producing a type error (function missing) at runtime.

Static type checking assumes that the runtime environment is adequately described by the compile time information. But predicting the future is perilous!

Here's one more limitation:

The compiler may generate code that assumes the runtime type is the static type.

Assuming the static types are "correct" provides many opportunities for optimization, but these optimizations can be limiting. A good example is proxy objects, e.g. remoting. Say you wish to have a local proxy object that forwards method invocations to a real object in another process. It would be nice if the proxy were generic (so it can masquerade as any object) and transparent (so that existing code does not need to know it is talking to a proxy). But to do this, the compiler cannot generate code that assumes the static types are correct, e.g. by statically inlining method calls, because that will fail if the object is actually a proxy.

Examples of such remoting in action include ObjC's NSXPCConnection or C#'s TransparentProxy (whose implementation required a few pessimizations in the runtime - see here for a discussion).

When the codegen is not dependent on the static types, and you have facilities like message forwarding, you can do lots of cool stuff with proxy objects, debugging, etc.

So that's a sampling of some of the stuff you can do if you are not required to satisfy a type checker. The limitations are not imposed by static types, but by enforced static type checking.

share|improve this answer
answered Oct 5 at 4:34

community wiki

ridiculous_fish
show 1 more comment
feedback
up vote 15 down vote

Duck-typed variables are the first thing everyone thinks of, but in most cases you can get the same benefits through static type inference.

But duck typing in dynamically-created collections is hard to achieve in any other way:

>>> d = JSON.parse(foo)
>>> d['bar'][3]
12
>>> d['baz']['qux']
'quux'

So, what type does JSON.parse return? A dictionary of arrays-of-integers-or-dictionaries-of-strings? No, even that isn't general enough.

JSON.parse has to return some kind of "variant value" that can be null, bool, float, string, array of any of these types recursively, or dictionary from string to any of these types recursively. The main strengths of dynamic typing come from having such variant types.

So far, this is a benefit of dynamic types, not of dynamically-typed languages. A decent static language can simulate any such type perfectly. (And even "bad" languages can often simulate them by breaking type safety under the hood and/or requiring clumsy access syntax.)

The advantage of dynamically-typed languages is that such types cannot be inferred by static type inference systems. You have to write the type explicitly. But in many such cases—including this once—the code to describe the type is exactly as complicated as the code to parse/construct the objects without describing the type, so that still isn't necessarily an advantage.

share|improve this answer
edited Oct 3 at 20:34

community wiki

abarnert
18  
Your JSON parsing example can be easily handled statically by an Algebraic Data Type. – Matt Fenwick Oct 3 at 20:25
2  
OK, my answer wasn't clear enough; thanks. That JSValue is an explicit definition of a dynamic type, exactly what I was talking about. It's those dynamic types that are useful, not languages that require dynamic typing. However, it's still relevant that dynamic types cannot be automatically generated by any real type inference system, while most of the common examples people are trivially inferrable. I hope the new version explains it better. – abarnert Oct 3 at 20:37
4  
@MattFenwick Algebraic Data Types are pretty much restricted to functional languages (in practice). What about languages like Java and c#? – spirc Oct 5 at 6:25
4  
ADTs exist in C/C++ as tagged unions. This isn't unique to functional languages. – Clark Gaebel Oct 5 at 13:39
1  
@spirc you can emulate ADTs in a classical OO language using multiple classes that all derive from a common interface, run-time calls to getClass() or GetType(), and equality checks. Or you can use double dispatch, but I think that pays off more in C++. So you might have a JSObject interface, and JSString, JSNumber, JSHash, and JSArray classes. You would then need some code to turn this "untyped" data structure into an "application typed" data structure. But you would probably want to do this in a dynamically-typed language, too. – Daniel Yankowsky Oct 9 at 1:42
feedback
up vote 13 down vote

As every remotely practical static type system is severely limited compared to the programming language it is concerned with, it cannot express all invariants which code could check at runtime. In order to not circumvent the guarantees a type system attempts to give, it hence opts to be conservative and disallow use cases which would pass these checks, but cannot (in the type system) be proven to.

I'll make an example. Suppose you implement a simple data model to describe data objects, collections of them, etc. which is statically typed in the sense that, if the model says the attribute x of object of type Foo holds an integer, it must always hold an integer. Because this is a runtime construct, you cannot type it statically. Suppose you store the data described in YAML files. You create a hash map (to be handed to a YAML library later), get the x attribute, store it in the map, get that other attribute which just so happens to be a string, ... hold a second? What's the type of the_map[some_key] now? Well shoot, we know that some_key is 'x' and the result hence must be an integer, but the type system can't even begin to reason about this.

Some actively researched type systems may work for this specific example, but these are exceedingly complicated (both for compiler writers to implement and for the programmer to reason in), especially for something this "simple" (I mean, I just explained it in one paragraph).

Of course, today's solution is boxing everything and then casting (or having a bunch of overriden methods, most of which raise "not implemented" exceptions). But this isn't statically typed, it's a hack around the type system to do the type checks at runtime.

share|improve this answer
edited Oct 6 at 10:14

community wiki

2 revs, 2 users 92%
delnan
show 7 more comments
feedback
up vote 6 down vote

There is nothing you can do with dynamic typing that you can't do with static typing, because you can implement dynamic typing on top of a statically typed language.

A short example in Haskell:

data Data = DString String | DInt Int | DDouble Double

-- defining a '+' operator here, with explicit promotion behavior
DString a + DString b = DString (a ++ b)
DString a + DInt b = DString (a ++ show b)
DString a + DDouble b = DString (a ++ show b)
DInt a + DString b = (show a ++ b)
DInt a + DInt b = DInt (a + b)
DInt a + DDouble b = DDouble (fromIntegral a + b)
DDouble a + DString b = DString (show a ++ b)
DDouble a + DInt b = DDouble (a + fromIntegral b)
DDouble a + DDouble b = DDouble (a + b)

With enough cases you can implement any given dynamic type system.

Conversely, you can also translate any statically typed program into an equivalent dynamic one. Of course, you would lose all compile-time assurances of correctness that the statically typed language provides.

Edit: I wanted to keep this simple, but here are more details about an object model

A function takes a list of Data as arguments and performs calculations with side effects in ImplMonad, and returns a Data.

type Function = [Data] -> ImplMonad Data

DMember is either a member value or a function.

data DMember = DMemValue Data | DMemFunction Function

Extend Data to include Objects and Functions. Objects are lists of named members.

data Data = .... | DObject [(String, DMember)] | DFunction Function

These static types are sufficient to implement every dynamically typed object system I'm familiar with.

share|improve this answer
edited Oct 5 at 20:22

community wiki

2 revs
NovaDenizen
4  
You are mixing concepts of dynamic typing with weak typing in your example. Dynamic typing is about operating on unknown types, not defining a list of allowed types and overloading operations between those. – 
gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.