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, andTerm
represents the type of the parameters. - ParamList: A list of ParamBindings, separated by commas.
- Definition: Starts with the
def
keyword, 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:
map
is the function name.[A: 'Type]
is the implicit parameter list:A
is a type parameter, constrained by'Type
, meaning it can be any type in the universe of types.- The compiler infers the type
A
based on the type of the list passed to the function. (list: List[A], transform: A -> A)
is the explicit parameter list:list
is a list of elements of typeA
.transform
is a function that takes a value of typeA
and 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 thetransform
function.
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
identity
takes a single valuex
of typeA
and returnsx
. - The type
A
is inferred from the argument passed toidentity
, so the user doesn't need to explicitly provideA
when 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
sum
takes two valuesx
andy
of typeA
. - The type
A
is constrained by the contract'Add
, meaning thatA
must implement theadd
method. - The function returns the result of adding
x
andy
usingA
'sadd
method.
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 typeA
that satisfies the'Add
contract. - This allows users to use
+
in the same way they would for built-in numeric types, but it applies to any type that defines anadd
method.
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:
square
is the function name.[R: 'Type, A: 'Mul(A, R)]
are implicit parameters:R
is a type, andA
is a type constrained by the'Mul(A, R)
contract, meaning thatA
must implement amul
method that returns a value of typeR
.(self: A)
is the explicit parameter.- The return type is
R
, the result of multiplyingself
by itself using themul
method.
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.