What can I do with dynamic typing that I can not do with static typing?
programmers.stackexchange.com
programmers.stackexchange.com
Another dimension is the explicitness of your variable languages. This isn't quite orthogonal since static languages have to allow you to specify a type and dynamic languages have to have some way for you to not specify a type but many static languages allow you to infer a type and many dynamic languages allow you to specify types. (You can always use asserts if they don't explicitly allow it).
My point is that inferred types always have the potential to provide surprising results. It is unlikely in static languages, but it's possible. If you've defined "square x = x * x" it will allow you to square anything that has the splat operator defined for, even if that would be nonsensical. For that reason inferred static languages generally don't overload the splat operator, so it's a bad example. But I hope you still get my point.
And the mix can produce interesting results. For eg. in Python:
def double(x):
return x * 2
print double(2) # => 4
print double("X") # => XX
If you were expecting a number to be returned then this isn't good :(The * operator isn't overloaded in Perl so instead we get:
sub double { $_[0] * 2 }
say double(2); # => 4
say double('X'); # => 0
Perl is more weakly typed than Python so 'X' get coerced to zero with the multiplication operation. So at least a number is returned but I would prefer a warning or error: use warnings;
say double('X'); # produces runtime warning
use warnings FATAL => 'all';
say double('X'); # produces runtime exceptionMy "whoa" moment with python typing came when I implemented euclid's algorithm (for the gcd of two numbers). Shortly later, I wrote a polynomial class, and then I was able to use my original function on polynomials without changing a single character
In some statically typed languages that would be possible if I'd thought to write my original function generically. But at the time I hadn't even thought about calling it with anything other than integers.
gcd :: Num a => a -> a -> a
which means it is a function that takes 2 a's and returns an a, with the restriction that a is of typeclass[1] Num, that is numbers.
So, very much possible and assumed without thinking about it too much.[1] A typeclass is a like an interface.
gcd :: Integral a => a -> a -> a
since writing Euclid's algorithm requires using `mod` or `rem`, both of which only work for integral types (NB. there are many integral types beside ints, for example polynomials, formal power series and gaussian integers).The Integral class captures the mathematical idea of a Euclidean domain, which can loosely be thought of as "number systems for which the Euclidean algorithm works".
Nevertheless, writing generic code is of course possible with static types.
If in addition you want to write your gcd function using the Euclidean algorithm, you need to specialize again to Euclidean domains (of which polynomials and power series are an example).
The `gcd` function in the Haskell base library operators on Integral types, which are the programmatic representation of Euclidean domains, so I would argue that it is "general enough".
http://www.youtube.com/watch?v=XGyJ519RY6Y (video only, the presentation was liveish coding in emacs and I haven't found the source files)
Agda of course pursues the anti-thesis of dynamic typing.
If the goal is to be able to invoke a method on an object without checking for anything other than whether a method with the correct signature exists, that isn't a feature that's supplied by dynamic typing. It's supplied by duck typing, which is yet another axis (alongside dynamic/static and strong/loose). A popular example of a statically typed language that has had duck typing for a very long time is Objective-C.
Metaprogramming is prevalent in python/ruby, regardless of how confusing it is, because it is super useful. Being able to dynamically construct types and classes (e.g. via decorators in python) allows you to do tons of stuff that is otherwise really hard for library writers, even though you shouldn't be using metaprogramming all over the place day-to-day. Rails, SQLAlchemy and a ton of other libraries/frameworks use metaprogramming all over the place.
Static languages lack this ability, but because it's so useful, they end up re-implementing the same thing anyway but using reflection/bytecode-manipulation/extra-compile-hooks/code-generation/etc.. like in Spring/AspectJ/Play/ASP.NET!
Although the end result is kinda the same, I'd argue metaprogramming via generating java source code is way harder than metaprogramming in python, where creating a new class and setting its methods or descriptors is as easy as creating/fleshing out any other object. Given how powerful/useful metaprogramming is, I think that that's the biggest gain of dynamic over static languages.
cough MetaOCaml [1] cough
cough first-class modules in OCaml [2] cough
explanation -- MetaOCaml provides what is essentially a typed eval for Objective Caml. OCaml's first-class modules provide the ability to construct types, classes, and modules at run time.
In other words, statically typed languages do have this ability.
[2] http://caml.inria.fr/pub/docs/manual-ocaml-4.00/manual021.ht...
I'd like to say the following to the creators of similar frameworks: Just give me the interface and I will implement it. And keep your hands off my internals!
Part of why I suggest C# is that a lot of .NET libraries make heavy use of this functionality. Windows Communication Foundation, NHibernate, and Moq come to mind. So my impression had always been that it was approximately the same level of reflection, just with perhaps more syntactic sugar in one case.
Static typing is safer (more compile-time checks) but more verbose. Type errors are found by the compiler before the program runs.
With dynamic typing you write less, but requires better understanding of the underlying types so you don't get runtime errors. Type errors propagate through the program until it generates a runtime exception or wrong results.
No, explicit typing is more verbose. Static typing does not mean java. Many statically typed languages have type inference, and have for a long time. They are no more verbose than dynamically typed languages.
def square(x):
return x * x
Haskell (statically typed): square x = x * x f = lambda r, a, b: r(a), r(b)
f(lambda x: x, 5, "Test")
in Haskell without writing types?Datatypes are like types but without the structure or logic. They don't prove anything and are basically just sets. You have to bring your own rules. Dynamic languages are unityped, you can see this by noting working only on just a top type shares many properties (not all, e.g. flexibility) of being in a dynamic language. Dynamic languages however can have datatypes, which make sure operations on the collections a particular object inhabits makes sense according to rules the language designer defines.
For your example, in a dynamic language I can state that r,a and b all belong to the same type but their datatypes may vary from moment to moment. In a statically typed language I cannot. Without types a ST language will assume that a and b are the same universal type. But I could give just a a top type and then your example would work. But I lose type safety. To get it back I could use interfaces, inheritance or algebraic datatypes each with their own pros and cons.
The difficulty with doing this in a statically typed system is that once you've fixed the type of the function you pass in, you can't then apply it to two different types. The standard way to solve this would be to specify that the function you pass in is valid for all types [1]
f :: (forall s. s -> s) -> a -> b -> (a,b) -- [2]
f r a b = (r a, r b)
f id 5 "Test" -- returns (5,"Test")
However, the challenge also specified that you do it without writing a type signature for `f`. I admit that in this case I am pretty stumped. The closest I can get is to first define a wrapper data S = forall a. Show a => S a
instance Show S where Show (S a) = show a
and then write f r a b = (r a, r b)
f id (S 5) (S "Test") -- returns (5,"Test")
but that involves wrapping everything up before passing it into the function, to disguise the fact that we really have different types.There's an interesting point to be made here, though - "wrapping everything up in one type" is essentially what dynamic type systems do, except that it's baked into the language instead of being implemented by the programmer on an as-needed basis.
Here, my declaration for the type S says "any type as long as it has a Show instance" (which allows it to be printed). I could instead have specified "any type that has a Num instance" which would mean that I could pass in a mix of ints and floats, and still do arithmetic with them. I get some of the benefits of dynamic typing (flexibility) but I still get to keep type checking.
The Python approach is "any type, and we'll check at runtime that it has the appropriate methods". So it's ultimately flexible, but as a result loses any form of compile-time checking.
Looking at it this way, I think that the challenge you've set me is one that cuts to the core of the difference between dynamic and static type systems. No, I can't write the function you want in a statically typed language without doing something clever with types. But the clever thing I have to do with types is re-invent duck typing, but in a limited way which still gives me compile-time checking.
Anyhow, thanks for the insightful comment. It certainly made me think...
[1] This requires the language extensions ExistentialQuantification and Rank2Types, but neither of those are particularly controversial.
[2] Note that the type signature
forall a. a -> a
is inhabited by exactly one function - the identity function. This carries over to the Python example too - the function `f` isn't safe to use unless the
first argument is the identity function, which makes it a bit of a useless function to define in the first place.This is imprecise - a dynamic language does not really need to "wrap everything in one type" - look at Dart or Groovy. What makes it 'dynamic' is the dynamic dispatch. All dynamic languages I know dispatch based on runtime argument types, but this is only one particular form of dynamic dispatch. Declaring types in dynamic language typically is used at runtime merely as an assertion, that at this point the argument must conform to the said type.
Dynamic dispatch differs from Polymorphic dispatch in the way that polymorphic dispatch takes in account the runtime type of the target (the object whose method you are calling), but the types of the parameters are fixed at compile time.
Many dynamic languages also provide a way to customize the dispatch logic (i.e. call a default handler if no match exists) - typically via some form of Meta Object Protocol. Often the dynamic languages provide features like higher order functions, continuations, pattern matching, etc. but this has nothing to do with their dynamicity - same features are available in some static languages as well.
If you want to faithfully translate the idea that 5 and "Test" are different values that can be passed into the same places but told apart by tests if you want, you might translate the second line to something like f (\x -> x) (Left 5) (Right "Test"), which does pass.
No, you don't. Type inference was invented in 1969. 43 years ago.
In python you just print
The "..." part means that the function has to figure out what the types of all the arguments are. printf accomplishes this by, at runtime, parsing the string format and deciding what types to use.
Contrast with a string representation function from a language like Haskell: show::(Show a) => a -> String
No matter what argument is provided to the function show, as long as it observes the type constraint (Show a), a sensible result will be returned. Since it is known at compile time whether the argument observes the class constraint, there's never a surprise that value is printed incorrectly.
I can write a 500 line C# program and it works reliably first time with no crashes, type inference errors or framework exceptions thrown. The only errors will be algorithmic or functional (i.e. based on requirements).
The same is not true with python.
I have the same experience with both. Go figure.
What's it like after that testing? How's the long term maintainability? I'm much more interested in possible problems - and it could go either way - in the long term than before I've finished writing it.
I tend not to write oodles of test cases up front in favour of simple scenario based tests applied later on. I add pre/post/invariant-condition checks in the code as I write it. It is tested incrementally by hand.
The condition checks prevent the what if's and tell you why something broke. The scenario tests ensure that it does what is asked of it.
I rarely get bugs raised against my code (7 this year out of about 112,000 lines of c# written). Not bad!
Update: Just noticed you said type and not typo related! Still use strict does catch a lot of silly errors that unfortunately do slip into code with other dynamic languages.
Please don't assume that what was written was wrong, especially when it wasn't.
So an application, say a web application in Python using some framework, what kind of type-related coding mistakes could you make? And which ones would be subtle enough to only be caught after it's in the customer's hands? (assuming a reasonable minimum of testing before that)
I'm asking not out of disbelief, but just because I'm having a real hard time coming up with an example that makes me "yeah I'd fall for that and only realize until after I delivered".
Is it possible that maybe, just general good coding style in Python makes it so that either you don't even consider sending a `str` to a function that expects a `float`, or that if you do (something like that) it fails almost immediately with a run-time Exception, way before you think of delivering?
For most of my Python programs, once they're done, if you'd analyze them, the types sent over all code paths probably would be static :) I'd think it pretty strange if a program would suddenly call functions with different parameter types, depending on user-input, unless you actively decided that it should do so by design (like something that handles arbitrary JSON-like structures), in which case you probably intend to restrict the amount of types it will be intended to handle.
So in that sense, you end up with what is mostly a statically typed program in the end, even if written in a dynamic language, no?
One thing I do agree that if your program is written dynamically typed and intends to keep on being very dynamic about its typing even after it's done during run-time then, yes, it's probably almost impossible to make sure it does not crash or throw exceptions, given arbitrary user input. But in that case, often this is expected, and not the kind of program you'd consider an end-user application, but rather a sort of tool script thing that can indeed crash on unexpected input. Which is also useful :)
So, and correct me if I'm wrong, but I'd conclude that the difference is that with dynamic typing, there's less red tape during development. But a well written program intended for end users probably ends up with mostly static types de facto because otherwise it'd be fairly unpredictable. However, if you'd have used a statically typed language, you would not have to assume "well written" because the static typing brings guarantees with it.
One hidden (and seldom discussed) feature of static type systems is that in multi-team projects, they can enforce and document abstraction layers between teams. I've even seen static type systems act as impartial arbiters when an argument arises as to if a given change broke one side of an abstraction barrier, or if the change merely exposed a latent silent bug on the other side of the abstraction barrier.
$instance = new $classname(param1, param2)In short, you can cut down on abstraction layers when working with behaviors that are inherently runtime.
If static typing would be good enough, you would not need casts. A cast basically says: "Hey compiler forget about type checking, just believe me that this thing has that type."
In respect to any language, the database is inherently dynamic, because it doesn't share your type system and the schema can be redefined at any point after compilation of your code. A statically typed language doesn't magically change that. The best you can do is to write some classes in advance and then hope nothing gets out of sync.
So, to put it in simpler terms, some things are inherently dynamic, and dynamic languages make working with them easier.
(I am assuming that the types of values returned by a query are finite and known.)
cough Ur/Web [2] cough
Both these languages have strongly-typed queries. Database queries are not "inherently runtime".
If you don't know what type of data is stored in your database, your code is probably incorrect.
The way to go in .NET is the opposite direction: Entity Framework with Code First approach.
And smart use of data structures, algebraic types and composition for the latter. The benefit of this is you build a sort of theory for your game, sometimes it predicts and suggests things that would not have been obvious to you otherwise.
I think statically typed languages have a lot of ceremony around doing something, whereas dynamically typed languages just get out of your way.
Luckily there is a workaround for Javas static typing limitation: Casts. Every time a Java programmer uses a cast, you can see that static typing has limits.
(define curry (f . cargs) (lambda moreargs (apply f (append cargs moreargs))))