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
}