1. 11 Jun, 2020 1 commit
  2. 10 Jun, 2020 1 commit
  3. 28 May, 2020 2 commits
    • Yorick Peterse's avatar
      c16c540b
    • 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
  4. 03 May, 2020 1 commit
    • Yorick Peterse's avatar
      Fix license scanning build · 177d616a
      Yorick Peterse authored
      License scanning was broken due to Cargo not being installed, and the
      configuration no longer working with newer versions of the license
      finder Gem.
      177d616a
  5. 30 Apr, 2020 4 commits
  6. 27 Feb, 2020 1 commit
    • Yorick Peterse's avatar
      Use VirtualBox for both Windows and macOS · 1aac792c
      Yorick Peterse authored
      Using Windows containers in Docker has been a painful experience. Slow
      networks, no support for transparent networks due to the host being a
      VPS, building large containers taking forever, the list goes on.
      Meanwhile it costs me roughly €50 per month to keep the Windows CI
      runner online.
      
      Since we already have a Mac Mini at MacStadium (for free), we can just
      re-use that Mac and run Windows inside a VirtualBox VM. This allows us
      to work around the various Docker issues on Windows, and should reduce
      our monthly infrastructure costs to more or less €0 (excluding the €1-2
      I pay to AWS every month).
      
      As part of this we also re-organise the runner tags a bit: the runners
      now use three tags:
      
      1. "inko"
      2. "vbox" (to indicate it's a virtualbox runner)
      3. "macos" or "windows", based on the type of OS
      
      This ensures we don't accidentally pick up a shared runner that just
      uses a "windows" or "macos" tag.
      1aac792c
  7. 23 Feb, 2020 1 commit
    • Yorick Peterse's avatar
      Upgrade the "time" crate to 0.2.x · 5352ec7f
      Yorick Peterse authored
      Version 0.2.x of the "time" crate comes with an API that is quite
      different from version 0.1.x. This commit upgrades Inko to this new AP.
      As version 0.2.x does not support checking if DST is active, support for
      this has been removed. DST support is tricky, as it can vary based on
      where you are, politics (e.g. some countries may drop it), etc.
      
      As part of upgrading to time 0.2.x, some of the internals for obtaining
      timestamps has been changed. In particular, obtaining a timestamp and
      UTC offset has been merged into a single VM instruction. This should
      ensure that no funny business happens when obtaining a timestamp but not
      obtaining the UTC offset until a later point in time. The downside is
      having to allocate an array object, but the overhead of this should be
      minor.
      
      This fixes #188
      5352ec7f
  8. 05 Feb, 2020 1 commit
    • Yorick Peterse's avatar
      Remove type parameters when re-opening objects · d5c10f8b
      Yorick Peterse authored
      When implementing a trait for an object or when re-opening an object,
      the compiler required that you specify the type parameter names of the
      object. For example, to implement the Equal trait for Triple you would
      write:
      
          impl Equal for Triple!(A, B, C) {
            ...
          }
      
      The original idea of this syntax was to make it more clear that type
      parameters used in the body originated from the object, not the trait.
      
      Over time, I realised that this doesn't actually help and in fact makes
      the compiler's life more difficult in a bunch of places. In this commit,
      we remove the need for specifying the type parameters, allowing you to
      write the following instead:
      
          impl Equal for Triple {
            ...
          }
      d5c10f8b
  9. 31 Jan, 2020 1 commit
    • Yorick Peterse's avatar
      Update Cargo files for Rust 1.41 · 4541d246
      Yorick Peterse authored
      Rust 1.41 introduces a new more compact format for Cargo.lock. This
      commit takes advantage of that change, and bumps some crate versions
      while we're at it.
      4541d246
  10. 23 Jan, 2020 1 commit
    • Yorick Peterse's avatar
      First-class modules and remove top-level object · 278a1652
      Yorick Peterse authored
      Modules are now first-class objects in the VM. This allows us to remove
      the top-level object, clean up core::bootstrap, and remove
      core::globals (in favour of using core::bootstrap directly). As part of
      this we also remove Trait.implement(), instead implementing traits using
      compiler generated code. Trait.implement() could not be safely used
      anyway, as it only copied over default methods; without verifying if all
      required methods are implemented.
      
      All of this makes it easier to implement the self-hosting compiler: the
      top-level object no longer needs to be supported, and we can treat
      core::bootstrap like any other module. This in turn allows us to use a
      more efficient way of storing type information in various places.
      278a1652
  11. 22 Jan, 2020 4 commits
    • Yorick Peterse's avatar
      Clean up Nil by splitting it into NilType and Nil · a97f10be
      Yorick Peterse authored
      NilType is now the base object/prototype that Nil is an instance of,
      instead of Nil being a direct instance of Object. This allows us to
      implement traits and redefine methods on the type of Nil (= NilType),
      instead of Nil itself. This in turn allows the compiler to use a more
      efficient way of representing types versus instances.
      
      In type signatures you can still use Nil (or NilType, though Nil is
      preferred), as this signals that you expect or return something
      compatible with Nil, which is only Nil itself.
      a97f10be
    • Yorick Peterse's avatar
      Fixed broken/outdated compiler test · b3e718f6
      Yorick Peterse authored
      b3e718f6
    • Yorick Peterse's avatar
      Restore Boolean "if" instruction cleanup · ddedead0
      Yorick Peterse authored
      By introducing the virtual "if" instruction we can clean up Boolean so
      that we no longer have to refine the True and False instances. If we
      also clean up Nil in a similar way it becomes easier to rewrite the
      compiler in Inko, as it no longer needs to support the refining of
      random objects.
      
      This was originally introduced in
      ecd11a07, but later reverted. In this
      commit we re-introduce these changes. Refining True and False makes
      various compiler internals in the self-hosting compiler less efficient,
      as it has to support refining any object even when this only happens for
      True, False, and Nil. By cleaning up True and False we can reduce this
      down to just Nil, making it easier to find a way of cleaning that up
      too.
      ddedead0
    • Yorick Peterse's avatar
      Don't use shared Windows runners · d30f885c
      Yorick Peterse authored
      With the release of shared Windows runners on GitLab.com, using runners
      with _just_ the "windows" tag results in CI using these shared runners.
      The shared runners don't have the right software installed, and are
      still in beta. To prevent using these, we now also require the runners
      to specify the "inko" tag.
      d30f885c
  12. 11 Jan, 2020 1 commit
    • Yorick Peterse's avatar
      Remove support for constant receivers · f46694ef
      Yorick Peterse authored
      This removes support for syntax such as `A::B` where `A` and `B` are
      constants. Inko has not allowed defining nested types or constants in
      types for a while, and this syntax was only used in a few places to
      bootstrap the runtime. Removing support for this syntax simplifies both
      the parser and compiler quite a bit.
      f46694ef
  13. 07 Jan, 2020 1 commit
  14. 05 Jan, 2020 2 commits
  15. 04 Jan, 2020 1 commit
    • Yorick Peterse's avatar
      Add Array.reverse_iter · f6bf5de0
      Yorick Peterse authored
      The method Array.reverse_iter returns an Iterator that iterates over the
      values in an Array in reverse order.
      f6bf5de0
  16. 02 Jan, 2020 4 commits
    • Yorick Peterse's avatar
      Remove support for extending traits · 4af9354f
      Yorick Peterse authored
      Now that the Ruby compiler defines types using multiple passes, there no
      longer is a need for forward declaring and extending traits. Extending
      traits could also lead to odd behaviour when adding default methods. For
      example:
      
          trait A {}
      
          object Foo {}
      
          impl A for Foo {}
      
          trait A {
            def thing -> Integer {
              10
            }
          }
      
      Here type "Foo" would not have access to method "thing", because it is
      added after "A" is implemented for "Foo".
      
      Removing support for this entirely solves this problem, and we no longer
      need it anyway.
      4af9354f
    • Yorick Peterse's avatar
      Remove forward trait declaration from std::net::ip · 9f152024
      Yorick Peterse authored
      This is no longer needed now that the Ruby compiler defines types in
      multiple passes.
      9f152024
    • Yorick Peterse's avatar
      Rename a few AST nodes to use the pattern DefineX · 679ccf7b
      Yorick Peterse authored
      This ensures all these nodes use the same naming pattern
      (DefineAttribute, DefineMethod, etc), instead of sometimes being called
      DefineX while other times being called XDefinition.
      679ccf7b
    • Yorick Peterse's avatar
      Support circular types in the Ruby compiler · 81a8fd1e
      Yorick Peterse authored
      This adds very basic (and super hacky) support for circular types to the
      Ruby compiler, which should be _just_ enough to allow us to continue
      work on the self-hosting compiler. Circular types can arise as follows:
      
          object TypeParameter {
            @required_traits: Array!(TraitType)
          }
      
          object TraitType {
            @type_parameters: Array!(TypeParameter)
          }
      
      Without support for circular types code like this won't compile.
      
      The changes needed to make this work are a total hack, and in some cases
      will lead to some duplicate work being done (with the results being
      discarded). Since this code will only stick around briefly, this is fine
      for now.
      81a8fd1e
  17. 01 Jan, 2020 1 commit
    • Yorick Peterse's avatar
      Allow hashing of any object · def89e1f
      Yorick Peterse authored
      This was technically already supported, but would trigger a panic for
      non-permanent objects. By allowing the hashing of any object we simplify
      the API, and make it easier to hash complex objects without having to
      manually implement the Hash trait for such structures.
      def89e1f
  18. 30 Dec, 2019 2 commits
  19. 27 Dec, 2019 2 commits
    • Yorick Peterse's avatar
      Revert Boolean and Nil cleanup · 22328acc
      Yorick Peterse authored
      I thought this would make the self-hosting compiler easier to implement,
      but it turns out this may not be the case. If we use two objects per
      type (one for the "class" and one for the instance), then yes it will
      clean things up. But this two object approach complicates the compiler
      in various other places. Balancing the two, I prefer being able to
      refine random objects as this means fewer VM changes are needed for for
      example Boolean and Nil.
      22328acc
    • Yorick Peterse's avatar
      Split Nil into Nil and NilType · 8f9109a4
      Yorick Peterse authored
      Nil is still the singleton instance, but its prototype is set to NilType
      instead of Object. By splitting Nil this way we no longer refine
      instances of ad-hoc instances, instead only refining objects defined
      directly using the `object` keyword.
      8f9109a4
  20. 26 Dec, 2019 1 commit
    • Yorick Peterse's avatar
      Clean up Boolean using an "if" instruction · ecd11a07
      Yorick Peterse authored
      By introducing the virtual "if" instruction we can clean up Boolean so
      that we no longer have to refine the True and False instances. If we
      also clean up Nil in a similar way it becomes easier to rewrite the
      compiler in Inko, as it no longer needs to support the refining of
      random objects.
      ecd11a07
  21. 22 Dec, 2019 3 commits
  22. 20 Dec, 2019 4 commits