
What can I do with dynamic typing that I can not do with static typing? - pooya72
http://programmers.stackexchange.com/questions/167305/what-can-i-do-with-dynamic-typing-that-i-can-not-do-with-static-typing
======
bryanlarsen
Lots of people don't realize that strong/weak typing is orthogonal to
static/dynamic typing. Strong typing systems will throw an error if the wrong
type is used. A weakly typed language will attempt to coerce or cast the
variable. Python is much more strongly typed than both Javascript and C.

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.

~~~
draegtun
_re: strong/weak/dynamic typing, coercion & overloading_

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 exception

------
lmm
You can use a function with different types in a way the original programmer
didn't think about. Obviously this has good and bad aspects.

My "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.

~~~
Robin_Message
Playing statically-typed advocate for a moment: in Haskell, you wouldn't have
had to put a type down for the function, and the compiler would have inferred
the type

    
    
        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.

~~~
qznc
Well, pray that the library function is general enough. Unfortunately, this is
not the case for your gcd type, as gcd can be generalized to arbitrary
commutative rings.

Nevertheless, writing generic code is of course possible with static types.

~~~
crntaylor
Well... kind of. You _can_ define a gcd operation on arbitrary commutative
rings, but two elements of the ring don't necessarily have a unique gcd. If
you want a unique gcd for any pair of elements, you need to specialize your
commutative ring to a unique factorization domain.

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".

------
lihaoyi
The main thing that I see dynamic languages as having over static languages is
metaprogramming. See [http://stackoverflow.com/questions/8276387/statically-
typed-...](http://stackoverflow.com/questions/8276387/statically-typed-
metaprogramming).

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.

~~~
colanderman
_Static languages lack this ability_

 _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.

[1] <http://www.metaocaml.org/>

[2] [http://caml.inria.fr/pub/docs/manual-
ocaml-4.00/manual021.ht...](http://caml.inria.fr/pub/docs/manual-
ocaml-4.00/manual021.html#toc81)

~~~
icebraining
Could you please stop with the "cough" parts? It's rude and it adds nothing to
the discussion.

------
darkstalker
Both can do the same (they are turing complete), but some things are harder to
do in statically typed languages because of the underlying implementation,
like reflection or eval().

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.

~~~
crntaylor
Python (dynamically typed):

    
    
        def square(x):
            return x * x
    

Haskell (statically typed):

    
    
        square x = x * x

~~~
icebraining
Can you do

    
    
      f = lambda r, a, b: r(a), r(b)
      f(lambda x: x, 5, "Test") 
    

in Haskell without writing types?

~~~
crntaylor
For those who don't realise, the challenge is to write a function `f` that
takes three arguments, the first of which is a function which is applied to
the second and third arguments and returned in a pair.

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.

~~~
ddimitrov
>> 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.

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.

------
PommeDeTerre
You can more easily make type-related coding mistakes that won't be caught
until after your app is in the customers' hands.

~~~
tripzilch
I believe you, because that intuitively makes sense, but can anyone give me a
typical real-world example where this could easily happen?

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.

~~~
KMag
Some advantages of static typing show up in programming in the small. Errors
in forgetting to check for null/nil/None are a common class of errors that are
caught by many static type systems. Switch/match statements that leave out
possibilities without providing a default (especially if a new value is added
to an enum) are also common errors that are caught by some type checkers.

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.

------
BerislavLopac
The crucial moment in my understanding of dynamic languages was when I was
learning about the Abstract Factory pattern by implementing it in PHP. After
writing different factory classes and all the shenanigans I've realized that
all of that can be replaced with a simple

    
    
      $instance = new $classname(param1, param2)

------
ddimitrov
In a dynamic languages you can do dynamic dispatch - i.e. dispatching based on
the actual runtime type of the arguments of a function, as opposed on the
declared or statically inferred type. That among other things obviates the
need for the visitor pattern and can also be used as rudimentary form of
pattern matching.

------
gambler
You can generate fieleds of ORM objects based on the values returned by a
query. If you're writing a game, you can represent in-game objects directly
with language objects and add more properties and methods when they're
modified.

In short, you can cut down on abstraction layers when working with behaviors
that are inherently runtime.

~~~
tedunangst
Just use a dictionary or hashtable.

~~~
icebraining
But then, what have you really gained? A more cumbersome syntax? Because
that's still unverifiable by the compiler.

~~~
tedunangst
That's kind of my point.

------
ajanuary
The the core of it for me is you can almost always do x under a particular
static type system. But you can almost always do the same thing under /any/
dynamic type system.

------
minhajuddin
Meta programming is much much easier in dynamically typed languages like ruby.
It completely eliminates the use of interfaces, because you can call any
method on any object. If you want to do it defensively, just check if the
object has the method you want on it before calling it.

I think statically typed languages have a lot of ceremony around doing
something, whereas dynamically typed languages just get out of your way.

------
qznc
What you can do with your statically typed language obviously depends on the
strength of the type system. Haskell for example is quite good, but Java is
very limiting.

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.

------
silverlake
In mainstream statically-typed languages, you can't build a generalized curry
function.

(define curry (f . cargs) (lambda moreargs (apply f (append cargs moreargs))))

~~~
tedunangst
C++ isn't mainstream?

