Consider reworking keyword arguments using an "undefined" type
Summary
Keyword arguments currently use a hash map to map the names to their corresponding positional arguments. Instead of using a hash map, the compiler should generate a list of positional arguments. Optional arguments not provided will be set to an "undefined" value.
Motivation
Mapping keyword argument names to their positional arguments is expensive, and complicates the code used for running blocks. This complexity affects all method calls to a certain degree, even when no keyword arguments need to be looked up.
Using "undefined" values allows us to fill up unspecified optional parameters, removing the need for hash map lookups. For dynamic calls one would have to manually fill up arguments, or pass the appropriate values. This will make dynamic calls more cumbersome, but they are likely to be used rarely due to Inko's gradually typed nature. This could also be made easier by having the compiler take care of this at runtime somehow.
In short: this setup will speed up method calls, without having to change the code generated for optional arguments.
Implementation
The "undefined" type is a NULL pointer in the VM. Registers and locals are zeroed
out, removing the need for writing any additional values to set them to the "undefined"
type by default. This means we don't have to change the code generated for optional
arguments, and we only have to change LocalExists
to use something like
if local.is_undefined()
instead of if local.is_null()
.
The compiler will have to generate a list of positional arguments. For example, this:
def test(foo, bar = 10, baz = 20) {}
foo(10, baz: 30)
Would be translated into this:
def test(foo, bar = 10, baz = 20) {}
foo(10, undefined, 30)
For dynamic method calls (= sending a message to Dynamic
) we would have to disallow
the use of keyword arguments. Allowing this would result in arguments being set
incorrectly, without this being clear to the developer. This means that this code is
no longer valid:
let x = 'foo' as Dynamic
x.slice(length: 2, start: 0)
This would be invalid because slice
is defined as slice(start: Integer, length: Integer) -> String
,
and in this new setup the arguments would be mapped incorrectly.
Drawbacks
Dynamic method calls no longer support keyword arguments, and the Ruby compiler would have to be adjusted.