Spicy Sxpressions

The expressions discussed in the previous sections should feel familiar to hardware developers and software developers alike, but Spade also has a few expressions that are more unusual. Rust users can probably skip ahead, since these expressions are basically the same as Rust. For everyone else, let's talk about the more spicy 🌶️ expressions in Spade:

If expressions

"Control flow" in Spade is handled a little bit different than what you may be used to, unless you're coming from a Rust or functional programming background. In most languages you use an if expression to "conditionally" execute code if conditions happen. For example, an absolute value operation could be written as

def abs(x):
  result = x;
  if x < 0:
    result = -x;

However, in hardware, there is no way to "conditionally execute" a block of code. Hardware can only compute all branches, and select the corresponding output at the end, typically using a multiplexer

In order to reflect this, Spade is expression based and if expressions select values rather than conditionally executing branches. The above example would be written as

fn abs(x: int<16>) -> int<16> {
  if x < 0 {
    -x
  } else {
    x
  }
}

where the output of the function is the result of the if expression, i.e. -x if x is negative, and x if it is positive.

Conditionals being expressions means you can do some interesting things with them, for example, you can use them as parts of arithmetic:

let result = x + if add_one {1} else {0};

This particular example is strange and probably ill-advised, but this sort of technique can come in handy.

Blocks

The other unusual expression Spade has is the block which we've seen some examples of already; The abs function above has 3 blocks but you may not have thought of them as blocks.

A block is written as {} which contains a list of statements (variables, assertions etc.), and an optional final expression as the value of the block itself.

For example,

let result = {
  let sum = x + y;
  sum * z
}

This is effectively the same as writing let sum_prod = (x + y) * z but it allows you to break things into variables that are local to the block. This may seem strange at first, but hopefully makes more sense when you find out that these blocks are the bodies of both functions and if-expressions. For example you can of course define variables inside the body of if-expressions

let result = if op1 {
  let sum = x + y;
  sum * z
} else {
  x + z
}