Definition
In Saki, a definition is used to introduce named functions, constants, or operators within the type system. Definitions allow for explicit type annotations, parameter bindings, and a function body. The definition syntax is designed to handle explicit and implicit parameter lists, allowing for greater flexibility when defining generic, polymorphic functions.
Syntax of Definitions
The syntax for defining functions or operators in Saki is structured as follows:
ParamBindings ::= Ident+ ':' Term
ParamList ::= ParamBindings (',' ParamBindings)*
Definition ::= 'def' (Ident|OpIdent) ('[' ParamList ']')? ('(' ParamList ')')? '=' Term
- ParamBindings: Defines one or more parameters, where
Ident+represents one or more parameter identifiers, andTermrepresents the type of the parameters. - ParamList: A list of ParamBindings, separated by commas.
- Definition: Starts with the
defkeyword, followed by the function name (or operator), and can optionally include: - Implicit Parameters in square brackets (
[ParamList]), typically used for type-level parameters or constraints. - Explicit Parameters in parentheses (
(ParamList)), representing the values passed to the function. - The function body, which is the expression on the right-hand side of the
=symbol that defines the behavior of the function.
Components of Definitions
-
Identifier: The name of the function, constant, or operator. This can be a regular identifier or an operator (denoted as
OpIdent). -
Implicit Parameters: Parameters in square brackets (
[ ]) are implicit, meaning they are typically inferred by the compiler during function application. These parameters are often used for type parameters or contract constraints. In Saki, these parameters are generally related to the type system and are inferred based on the arguments passed to the function. -
Explicit Parameters: Parameters in parentheses (
( )) are explicit, meaning they must be provided by the caller. These are the values or expressions passed to the function when it is invoked. -
Return Type: The return type is specified after the parameter list and before the function body, indicating the type of the value returned by the function.
-
Function Body: The expression or series of expressions that define the actual behavior of the function.
Typical Example of a Function Definition
Consider a simple definition for a map function that operates on a list:
mapis the function name.[A: 'Type]is the implicit parameter list:Ais a type parameter, constrained by'Type, meaning it can be any type in the universe of types.- The compiler infers the type
Abased on the type of the list passed to the function. (list: List[A], transform: A -> A)is the explicit parameter list:listis a list of elements of typeA.transformis a function that takes a value of typeAand returns a new value of typeA.List[A]is the return type, indicating that the function returns a list of elements of typeA....represents the function body, which defines how the elements of the list are transformed by thetransformfunction.
Implicit and Explicit Parameters
In Saki, functions often have both implicit and explicit parameters. Implicit parameters allow for generic and polymorphic functions without needing to specify the type each time the function is called.
For example:
- The function
identitytakes a single valuexof typeAand returnsx. - The type
Ais inferred from the argument passed toidentity, so the user doesn't need to explicitly provideAwhen calling the function.
Type Constraints in Definitions
In Saki, contract universes can be used to impose constraints on the types of the parameters. For example, consider the following function that requires the type A to satisfy the 'Add contract (i.e., it must support an add operation):
- The function
sumtakes two valuesxandyof typeA. - The type
Ais constrained by the contract'Add, meaning thatAmust implement theaddmethod. - The function returns the result of adding
xandyusingA'saddmethod.
Operator Definitions
Operators can be defined similarly to functions, using the same syntax. For example, a custom addition operator could be defined as follows:
- The operator
+is defined for any typeAthat satisfies the'Addcontract. - This allows users to use
+in the same way they would for built-in numeric types, but it applies to any type that defines anaddmethod.
Higher-Level Universe in Definition
Consider a function that multiplies a value by itself, where the type A is constrained by the 'Mul contract universe:
squareis the function name.[R: 'Type, A: 'Mul(A, R)]are implicit parameters:Ris a type, andAis a type constrained by the'Mul(A, R)contract, meaning thatAmust implement amulmethod that returns a value of typeR.(self: A)is the explicit parameter.- The return type is
R, the result of multiplyingselfby itself using themulmethod.
This function is generic across any type A that supports multiplication, allowing it to work with numbers, matrices, or any other type that implements the 'Mul contract.