Many modern languages, especially languages with a functional heritage treat functions as first-class citizens. Spade, having a functional heritage via Rust is of no exception.
Lambda functions, once defined, can be passed around to other functions as arguments or stored in variables. This, among other things, allows you to customize the behavior of functions by passing other functions as values.
A prototypical example of that concept is a function, let’s call it transform, which operates on an array and transforms all its elements according to some formula. For example, given an array of numbers, the formula might add 1 to all elements, or double them, etc.
If the formula used for transformation was hard-coded within the transform function, this would not be very re-usable. Say you need to add 2 to the elements of an array, or triple them, you have to add new, different transform functions for each formula. This very quickly runs into scaling issues.
Now, what if we could pass the formula to be used from the outside to a formula-agnostic transform function?
You can think of this element-wise transformation formula simply as a (pure) function. Passed to the transform function, it will be applied to all elements and perform the same change on all values in the array.
A transform function1 may have a signature2 like this:
impl<#uint N> [Type used to represent unsigned integers. The generic parameter (<N>) specReturns the value of the first branch if the condition is true, otherwise the second branch. Note that unlike software languages, Spade does not have conditional execution. You do not conditionally assign values inside if expressiosn, you compute a value and return it.ies the number of bits; N] {
Functions together with Entities together with fn and Defines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. are the basic building blocks of Spade circuits. Unlike fn, entity can contain registers and therefore have state and unlike Defines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline., they do not have a statically known latency or Defines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. structure. and Defines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. are the basic building blocks of Spade circuits. Unlike entities and pipelines, functions have no internal state, they are combinational (pure). transform(self, f: impl Fn(Type used to represent unsigned integers. The generic parameter (<N>) specReturns the value of the first branch if the condition is true, otherwise the second branch. Note that unlike software languages, Spade does not have conditional execution. You do not conditionally assign values inside if expressiosn, you compute a value and return it.ies the number of bits) ->Type used to represent unsigned integers. The generic parameter (<N>) specReturns the value of the first branch if the condition is true, otherwise the second branch. Note that unlike software languages, Spade does not have conditional execution. You do not conditionally assign values inside if expressiosn, you compute a value and return it.ies the number of bits) -> [Type used to represent unsigned integers. The generic parameter (<N>) specReturns the value of the first branch if the condition is true, otherwise the second branch. Note that unlike software languages, Spade does not have conditional execution. You do not conditionally assign values inside if expressiosn, you compute a value and return it.ies the number of bits; N] {
// Implementation details
}
}
It takes the original array as an input, as well as a value called f which has the type:
impl Fn(Type used to represent unsigned integers. The generic parameter (<N>) specReturns the value of the first branch if the condition is true, otherwise the second branch. Note that unlike software languages, Spade does not have conditional execution. You do not conditionally assign values inside if expressiosn, you compute a value and return it.ies the number of bits) ->Type used to represent unsigned integers. The generic parameter (<N>) specReturns the value of the first branch if the condition is true, otherwise the second branch. Note that unlike software languages, Spade does not have conditional execution. You do not conditionally assign values inside if expressiosn, you compute a value and return it.ies the number of bits
as second argument. This means that f has to be a function that changes one value of type uint<32> to another. Using this function, the implementation of transform produces a new array of transformed values.
There are many more functions in that standard library of Spade that work along the same principles. For example on arrays, the standard library defines various transformations, filters and other utility functions.
Say we want to add 5 to each element of an uint<32> array. To call the transform function from above, we need to pass it a value of type impl Fn(uint<32>) -> uint<32>. But, how do we get a value of that type? The answer is a lambda function which can be defined as:
fnFunctions together with entityEntities together with fn and pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. are the basic building blocks of Spade circuits. Unlike fn, entity can contain registers and therefore have state and unlike pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline., they do not have a statically known latency or pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. structure. and pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. are the basic building blocks of Spade circuits. Unlike entities and pipelines, functions have no internal state, they are combinational (pure).|...| is a lambda function, a function written in-line to be passed to other functions to control their behaviour. The arguments to the function are the part between the ||value| {
value +.Add two values, producing a value of the same size while wrapping ifReturns the value of the first branch if the condition is true, otherwise the second branch. Note that unlike software languages, Spade does not have conditional execution. You do not conditionally assign values inside if expressiosn, you compute a value and return it. the result overflows. This is unlike +where the addition results in one more bit than the input type, guaranteeing no overflows.5
}
It is a function that has no name, it can be used directly in an expression. The parameters are specified between || and unlike non-lambda units, the type of parameters can be inferred. The body of the lambda function works exactly as the body of non-lambda functions.
To use this lambda function in the transform function we defined above, you can pass it directly as a value:
some_array
.transform(fnFunctions together with entityEntities together with fn and pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. are the basic building blocks of Spade circuits. Unlike fn, entity can contain registers and therefore have state and unlike pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline., they do not have a statically known latency or pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. structure. and pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. are the basic building blocks of Spade circuits. Unlike entities and pipelines, functions have no internal state, they are combinational (pure).|fnFunctions together with entityEntities together with fn and pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. are the basic building blocks of Spade circuits. Unlike fn, entity can contain registers and therefore have state and unlike pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline., they do not have a statically known latency or pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. structure. and pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. are the basic building blocks of Spade circuits. Unlike entities and pipelines, functions have no internal state, they are combinational (pure).|...| is a lambda function, a function written in-line to be passed to other functions to control their behaviour. The arguments to the function are the part between the ||value| {
value +.Add two values, producing a value of the same size while wrapping ifReturns the value of the first branch if the condition is true, otherwise the second branch. Note that unlike software languages, Spade does not have conditional execution. You do not conditionally assign values inside if expressiosn, you compute a value and return it. the result overflows. This is unlike +where the addition results in one more bit than the input type, guaranteeing no overflows.5
})
Lambda functions are values, which means that you can bind them to variables if desired. For example, you could use transform like this, first binding your lambda function to a variable named f:
letlet is used to define a variable. Spade infers the type of most variables from context, but you can also specifReturns the value of the first branch if the condition is true, otherwise the second branch. Note that unlike software languages, Spade does not have conditional execution. You do not conditionally assign values inside if expressiosn, you compute a value and return it.y the type with : <type> before the =. f =fnFunctions together with entityEntities together with fn and pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. are the basic building blocks of Spade circuits. Unlike fn, entity can contain registers and therefore have state and unlike pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline., they do not have a statically known latency or pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. structure. and pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. are the basic building blocks of Spade circuits. Unlike entities and pipelines, functions have no internal state, they are combinational (pure).|fnFunctions together with entityEntities together with fn and pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. are the basic building blocks of Spade circuits. Unlike fn, entity can contain registers and therefore have state and unlike pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline., they do not have a statically known latency or pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. structure. and pipelineDefines a pipeline. The number in in the parentheses is input-to-output latency of the pipeline. are the basic building blocks of Spade circuits. Unlike entities and pipelines, functions have no internal state, they are combinational (pure).|...| is a lambda function, a function written in-line to be passed to other functions to control their behaviour. The arguments to the function are the part between the ||value| {
value +.Add two values, producing a value of the same size while wrapping ifReturns the value of the first branch if the condition is true, otherwise the second branch. Note that unlike software languages, Spade does not have conditional execution. You do not conditionally assign values inside if expressiosn, you compute a value and return it. the result overflows. This is unlike +where the addition results in one more bit than the input type, guaranteeing no overflows.5
};
some_array
.transform(f)
This has advantages if you need the lambda function f in multiple close-by places.
To be more technically accurate, as defined it is a method on the array type. ↩︎
The version in the standard library of Spade is called map (a very common name for this function) and a bit more flexible than our example. ↩︎