I’m finally through the Big Cleanup (well, the first one, I’m sure there’ll be others) and have now arrived at the fun part: simplifying and generalizing based on the new cleaner infrastructure, and implementing completely new stuff. On the surface level I’ve made a few syntax changes to free up syntactic elements for some future constructs, including changing from -> to => for functions and : to in in for loops. Where before you would write

def this.size() -> this.elements.length;

def this.print_all() {
  for (elm : this)
    print(elm);
}

you must now write

def this.size() => this.elements.length;

def this.print_all() {
  for (elm in this)
    print(elm);
}

This is because I need the small arrow for delayed calls (such as process->run()) and I need the colon for keyword arguments. I have just completed a first shot at implementing keyword arguments which required a complete overhaul of how calls work. But now that they’re implemented they seem to be well worth the trouble.

To illustrate why keyword arguments are helpful consider this method from the implementation of towers of Hanoi:

def this.run(size) {
  this.build(0, size);
  this.move(0, 1, size);
  this.moves;
}

Quick, what does this do? If the algorithm being implemented here isn’t clear in your when you read this it’s not obvious - you have to know the solution to infer what the various arguments are and what this does. Now consider this:

def this.run(size:) {
  this.build(pile: 0, disks: size);
  this.move(from: 0, to: 1, disks: size);
  this.moves;
}

With keyword arguments you don’t have to guess how this works, the code tells you: there are piles with integer indices, we first build pile 0 with size elements and then and move that many disks from 0 to 1. Inferring this from the implementation without keywords is possible but requires you to think. Inferring it from the second requires no effort, it is clear from the code.

Keyword arguments are not a new invention, they are present in several popular languages. However, I would claim that they are used in an ad-hoc fashion, in part because languages like python give them to you for free. In python, any function, for instance

def run(self, size):
  ...

can be called with or without keyword arguments, in this case h.run(3) and h.run(size=3). This is good because it means that there’s no extra effort required to enable keyword arguments, but it’s bad because it means that you don’t have to put much thought into that part of your interface. There is also no established rule of thumb that I know of for when to use keywords in python – the google python style guide doesn’t even have an opinion, which really says something.

In neutrino, the intention is that the same way you make code more readable by using descriptive variable and method names, you should use keyword arguments. It’s just one of the mechanisms that can be used to make code self-documenting. The basic assumption is that keywords should be used whenever they aid code readability but left out if they are redundant, which is actually most of the time. Keywords are a public part of a method’s signature and should be chosen as carefully as the method’s name, since they are in some sense part of the name. Consequently, parameters that can be passed via keyword arguments are declared in one way (def foo(size:) => ...) and those that don’t in another (def foo(size) => ...). This is for two reasons: to make it explicit to users that it may not be obvious what these arguments mean so you might want to consider passing them using keywords, and the make it clear to the implementor that people will depend on those keyword names so there are compatibility concerns if you change them.

An interesting part of this will be to learn to use this myself, which may lead me to change how this works. However, I’m sure I’ll hang on to the idea of keyword arguments in some form, for code readability there is just no replacement for a construct like this.