Aliases

Aliases are a powerful feature that allow the expression of arbitrary types. In their simplest form, they may be used as syntactic sugar to reduce keystrokes and cognitive overhead from typing out a full type literal.

Fig 1:


# You can imagine that typing this out could be verbose/annoying.
alias IntsToTupleFn: function<|int, int| -> tuple<int, int>>

consumer prettyPrint(name: string, fn: IntsToTupleFn, arg1: int, arg2: int) {
  var res = fn(arg1, arg2);
  print("{name}({arg1}, {arg2}) -> {res}");
}

var swapped: IntsToTupleFn = lambda (a, b) -> (b, a);
prettyPrint("swapped", swapped, 1, 2);

var doubled: IntsToTupleFn = lambda (a, b) -> (2*a, 2*b);
prettyPrint("doubled", doubled, 1, 2);

Output:

[0.002s][warning][perf,memops] Cannot use file /tmp/hsperfdata_runner/6 because it is locked by another process (errno = 11)
swapped(1, 2) -> (2, 1)
doubled(1, 2) -> (2, 4)

Aliases are Syntactic Sugar

To be absolutely clear, Aliases are simply syntactic sugar as shown in the example above. They provide a mechanism for reducing the amount of boilerplate code that may need to be written where full type annotations are explicitly required. They also allow you to communicate some sort of "intent" where you would like to communicate the purpose of a value to other developers (or your future self) without actually committing to defining a fully new custom type (though aliases should be used for this purpose with caution). For example, below you'll see an example of using aliases to indicate that different int values have different interpretations.

Fig 2:


alias MPH : double
alias Hours : double # Arguably you should be using `duration::Duration`.
alias Miles : double

function timeTraveled(speed: MPH, distanceTraveled: Miles) -> Hours {
  return distanceTraveled / speed;
}

var speed: MPH = 15.0;
var distance: Miles = 60.0;
print(timeTraveled(speed, distance));

Output:

4.0

Overuse of Aliases Can be a Code Smell

Keep in mind that excessive use of aliases can be a code smell. If you are using an alias to try to encode some semantic distinction between values, it's very likely that you are writing highly bug-prone code as aliases do not provide any level of compile time verification that values of different alias types don't get accidentally conflated.

Fig 3:


var mySpeed: MPH = 15.0;
var myDistance: Miles = 60.0;

# Aliases provide zero compile-time protection from mistakenly passing these
# args out-of-order.
print(timeTraveled(myDistance, mySpeed));

Output:

[0.002s][warning][perf,memops] Cannot use file /tmp/hsperfdata_runner/6 because it is locked by another process (errno = 11)
0.25

See User Defined Types for an example of how to address this issue.