Skip to content
  • Yorick Peterse's avatar
    Replace the Dynamic type with the "Any" trait · 08385683
    Yorick Peterse authored
    The Any trait is a trait implemented by all objects automatically, and
    replaces all use of the Dynamic type. This means that Inko is now
    statically typed, instead of being gradually typed.
    
    The Any type is introduced as it can be cast to any other type. Using
    "Object" to represent something that could be anything does not work, as
    objects can't be casted to other objects. Any being implemented by all
    objects does allow one to cast it to an object.
    
    Argument types must now be known, meaning you either have to specify a
    default value, and explicit type, or both. In other words, this is no
    longer valid:
    
        def foo(number) { }
    
    For closures and lambdas the compiler still tries to infer the argument
    types based on how these blocks are used.
    
    The behaviour of omitting method return types also changed. Prior to
    this commit, leaving out a return type would result in it being inferred
    as Dynamic. Starting with this commit, omitting a method's return type
    results in:
    
    1. The compiler inferring the return type as Nil
    2. The compiler inserting a `return` at the end of the method, unless a
       `return` is already present
    
    Take for example this method:
    
        def init {
          @Number = 10
        }
    
    Prior to this commit, the return type of `init` would be `Dynamic` and
    the value returned would be `10` (an `Integer`). In this commit this is
    changed so that `init` is inferred to return `Nil`, and the actual
    return value is also `Nil` (not `10`). This matches how dynamic return
    types were used in the standard library so far: to signal we don't
    really care about the return value.
    
    For closures and lambdas, the compiler still tries to infer the return
    type based on the body; defaulting to Nil if no other type could be
    determined.
    
    == Reasons for moving to static typing
    
    Inko was gradually typed for a few reasons. First, when I started
    working on Inko I was leaning more towards making it a dynamic language
    similar to Ruby or Python. As I spent more time working on it, I
    realised I was starting to prefer a statically typed language more and
    more.
    
    I also felt that gradual typing would strike a balance between the rapid
    prototyping capabilities of a dynamic language, and the safety of a
    statically typed language. But in practise I found this not to be the
    case. For example, in dynamic languages (e.g. Ruby) I spend a lot of
    time jumping between running the code, and fixing silly errors such as
    the use of undefined local variables; something a statically typed
    language could easily detect.
    
    Inko using statically typed code as a baseline (instead of dynamically
    typed code) also made using dynamic types frustrating. Take this code
    for example:
    
        def add(a, b) {
          a + b
        }
    
        let result = add(10, 20)
    
    Here `a` and `b` are dynamically typed, so is the return type. Now
    imagine we want to print the results:
    
        import std::stdio::stdout
    
        def add(a, b) {
          a + b
        }
    
        let result = add(10, 20)
    
        stdout.print(result)
    
    This will fail to compile, as `stdout.print` expects a `ToString`, but
    `result` is a `Dynamic` and the compiler does not know if the runtime
    type implements `ToString`. This inevitably leads to a lot of cast
    expressions like so:
    
        import std::stdio::stdout
    
        def add(a, b) {
          a + b
        }
    
        let result = add(10, 20)
    
        stdout.print(result as Integer)
    
    If we're going to require developers to cast dynamic types almost every
    time they use them, they won't be all that useful. And if they're not
    useful, we should just get rid of them.
    
    This doesn't mean gradual typing is a bad idea. In fact, I think it's a
    great way of making a dynamically typed language more safe. TypeScript
    is a good example: it takes JavaScript (dynamically typed), and adds
    static typing on top. But supporting gradual typing in a statically
    typed language just doesn't bring any benefits, and makes certain
    optimisations more difficult or even impossible. And thus dynamic typing
    is no more.
    
    Fixes #194
    08385683