You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/Definition/Decorator.md
+43-43Lines changed: 43 additions & 43 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,4 +1,4 @@
1
-
##Decorator
1
+
# Decorator
2
2
3
3
A **decorator** in **Saki** is a higher-order function that takes another function as its argument and returns a new function with the same type signature. Decorators allow for the dynamic augmentation of functions with additional functionality, enhancing the behavior of the original function without modifying its core logic. In essence, a decorator has the type:
4
4
@@ -10,7 +10,7 @@ This means it takes a function of type `T -> R` and returns a function of the sa
10
10
11
11
Decorators in **Saki** can be applied directly to functions using a specific syntax. They enable modular code, where functionality can be extended in a reusable and composable manner.
12
12
13
-
###Syntax
13
+
## Syntax
14
14
15
15
The syntax for applying one or more decorators to a function is as follows:
16
16
@@ -27,7 +27,7 @@ def func: T -> R
27
27
28
28
-**`#[dec1, dec2, ..., decN]`**: Decorators are applied using a `#` followed by a list of decorators enclosed in square brackets. These decorators are applied to the function that follows.
29
29
30
-
###Typing Rules
30
+
## Typing Rules
31
31
32
32
Decorators must preserve the type signature of the functions they are applied to. If a function `func` has type `T -> R`, and a decorator `dec` transforms it while maintaining the same type, the resulting decorated function also has type `T -> R`. The typing rule can be formalized as:
33
33
@@ -37,61 +37,61 @@ $$
37
37
38
38
This ensures that the decorator takes in a function of type `T -> R` and returns a function with the same signature.
39
39
40
-
###Examples of Decorators
40
+
## Examples
41
41
42
-
1.**Ensuring Positive Input Values**
42
+
### Ensuring Positive Input Values
43
43
44
-
Consider a decorator that ensures that a function only operates on positive input values. This decorator checks if the input is greater than 0 and only calls the original function if the condition is satisfied. Otherwise, it returns `None`.
44
+
Consider a decorator that ensures that a function only operates on positive input values. This decorator checks if the input is greater than 0 and only calls the original function if the condition is satisfied. Otherwise, it returns `None`.
defsafeDivide(x: ℕ):Option[ℕ] =if x !=0thenSome(10/ x) elseNone
57
-
```
55
+
#[ensurePositive]
56
+
defsafeDivide(x: ℕ):Option[ℕ] =if x !=0thenSome(10/ x) elseNone
57
+
```
58
58
59
-
-**`ensurePositive`**: The decorator checks if the input is greater than 0. If true, it calls the original function; otherwise, it returns `None`.
60
-
-**`safeDivide`**: This function divides 10 by the input value, but using the decorator, it is guaranteed to only operate on positive integers. If the input is 0 or negative, it returns `None`.
59
+
-**`ensurePositive`**: The decorator checks if the input is greater than 0. If true, it calls the original function; otherwise, it returns `None`.
60
+
-**`safeDivide`**: This function divides 10 by the input value, but using the decorator, it is guaranteed to only operate on positive integers. If the input is 0 or negative, it returns `None`.
61
61
62
-
2.**Transforming Input Before Applying a Function**
62
+
### Transforming Input Before Applying a Function
63
63
64
-
The following example demonstrates how a decorator can be used to modify the input before passing it to the original function.
64
+
The following example demonstrates how a decorator can be used to modify the input before passing it to the original function.
-**`mapInput`**: This decorator takes a transformation function and applies it to the input before passing it to the original function.
76
-
-**`half`**: This function divides the input by 2, but with the decorator applied, the input is first doubled, so `half(5)` would return the result of `10 / 2`, which is `5`.
75
+
-**`mapInput`**: This decorator takes a transformation function and applies it to the input before passing it to the original function.
76
+
-**`half`**: This function divides the input by 2, but with the decorator applied, the input is first doubled, so `half(5)` would return the result of `10 / 2`, which is `5`.
77
77
78
-
3.**Transforming the Output of a Function**
78
+
### Transforming the Output of a Function
79
79
80
-
A decorator can also be used to modify the output of a function. In the following example, the decorator multiplies the function's result by 10.
80
+
A decorator can also be used to modify the output of a function. In the following example, the decorator multiplies the function's result by 10.
-**`mapOutput`**: This decorator takes a transformation function and applies it to the output of the original function.
92
-
-**`increment`**: This function adds 1 to the input, but with the decorator, the result is multiplied by 10. So, `increment(5)` results in `60`, as `(5 + 1) * 10 = 60`.
91
+
-**`mapOutput`**: This decorator takes a transformation function and applies it to the output of the original function.
92
+
-**`increment`**: This function adds 1 to the input, but with the decorator, the result is multiplied by 10. So, `increment(5)` results in `60`, as `(5 + 1) * 10 = 60`.
93
93
94
-
###Composition of Decorators
94
+
## Composition of Decorators
95
95
96
96
Decorators in Saki can be composed. Multiple decorators can be applied to the same function in sequence. This means that each decorator applies its transformation on the function that results from the previous decorator.
97
97
@@ -108,7 +108,7 @@ In this case:
108
108
109
109
If the input to `safeDivideAndScale` is `2`, the function returns `Some(50)` (since `10 / 2 = 5` and `5 * 10 = 50`). If the input is `0`, the function returns `None` due to `ensurePositive`.
110
110
111
-
####Contract Universes and Decorators
111
+
### Contract Universes and Decorators
112
112
113
113
In Saki, decorators can leverage **contract universes** to enforce behavior through constraints. For example, decorators like `ensurePositive` can depend on contract universes such as `'GreaterThan`, ensuring that the function operates only on types that support comparison.
In **Saki**, function overloading is introduced by leveraging a new type construct called the **superposition type** ($A \oplus B$), which allows a single function to operate over multiple input types, providing different behaviors based on the types of arguments passed to the function. This generalizes traditional function overloading, often found in programming languages, into the type-theoretic framework of **Martin-Löf Type Theory (MLTT)**. The superposition type formalizes this behavior, ensuring that function overloading is type-safe and rigorous, enabling polymorphism at the type level.
4
+
5
+
## Superposition Types
6
+
7
+
The **superposition type** $A \oplus B$ encapsulates the ability of a function to accept multiple types as input and return different types depending on the context of the application. Unlike a **sum type** ($A \sqcup B$), which restricts a term to belong to either $A$ or $B$, the superposition type dynamically resolves the term’s type based on the argument passed, making it applicable multiple times for different types.
8
+
9
+
For example, if a function can handle both integer and string types, its type would be expressed as $Int \oplus String$, meaning that the function can accept either type, and the appropriate behavior will be applied based on the argument.
10
+
11
+
### Typing Rules for Superposition Types
12
+
13
+
The rules for superposition types enable the safe typing and application of overloaded functions in the type system. These rules are as follows:
14
+
15
+
1.**Term Typing for Superposition Types**:
16
+
17
+
$$
18
+
\frac{\Gamma \vdash t: A \quad \Delta \vdash t: B}{\Gamma, \Delta \vdash t: A \oplus B}
19
+
$$
20
+
21
+
If a term $t$ can be typed as both $A$ in context $\Gamma$ and $B$ in context $\Delta$, then $t$ can be assigned the superposition type $A \oplus B$ in the combined context $\Gamma, \Delta$. This allows the function to behave polymorphically, with its type determined by the argument.
22
+
23
+
2.**Function Overloading with Superposition**:
24
+
25
+
$$
26
+
\frac{\Gamma \vdash t: A \rightarrow B \quad \Delta \vdash t: A \rightarrow C}{\Gamma, \Delta \vdash t: A \rightarrow (B \oplus C)}
27
+
$$
28
+
29
+
This rule allows a function to have multiple return types based on the context of its application. The function `t` can return either `B` in context $\Gamma$ or `C$ in context $\Delta$, and the final type is resolved as $B \oplus C$ depending on the argument.
30
+
31
+
3.**Function Application for Overloaded Functions**:
32
+
33
+
$$
34
+
\frac{\Gamma \vdash f: (A \rightarrow B) \oplus (C \rightarrow D) \quad \Gamma \vdash t: A}{\Gamma \vdash f\ t: B}
35
+
$$
36
+
37
+
This rule governs the application of overloaded functions. If a function `f` has a superposition type $(A \rightarrow B) \oplus (C \rightarrow D)$, applying it to an argument of type `A` resolves the function to return type `B`.
38
+
39
+
4.**Subtyping for Superposition Types**:
40
+
41
+
$$
42
+
(A \sqcup B) <: (A \oplus B)
43
+
$$
44
+
45
+
This rule states that a sum type $A \sqcup B$, which represents an exclusive choice between `A` and `B`, is a subtype of the corresponding superposition type $A \oplus B$. Superposition types provide more flexibility, allowing the function’s behavior to be dynamically resolved based on the context.
46
+
47
+
## Example of Function Overloading with Superposition
48
+
49
+
Consider the following example where a function `f` can take either an integer or a string as input and perform different operations:
50
+
51
+
```scala
52
+
deff(x: Int):Int= x +1
53
+
deff(x: String):String= x +"!"
54
+
```
55
+
56
+
In **Saki**, this behavior would be captured by a superposition type:
57
+
58
+
```scala
59
+
f: (Int->Int) ⊕ (String->String)
60
+
```
61
+
62
+
When called with an integer, `f(5)` returns `6`; when called with a string, `f("Hello")` returns `"Hello!"`.
63
+
64
+
### Overloading via `let-in` Expressions
65
+
66
+
The **`let-in` expression** in Saki can also incorporate superposition types, allowing for function overloading in local contexts. The general form of the `let-in` expression is:
67
+
68
+
$$
69
+
\text{let } x = t_1 \text{ in } t_2
70
+
$$
71
+
72
+
If `t_1` has a superposition type, the variable `x` inherits this type, and the body `t_2` must handle each possible type that `x` may take on.
73
+
74
+
#### Typing Rule for Overloaded `let-in`
75
+
76
+
$$
77
+
\frac{\Gamma \vdash t_1 : A \oplus C \quad \Gamma, x:A \vdash t_2 : B \quad \Gamma, x:C \vdash t_2 : D}{\Gamma \vdash \text{let } x = t_1 \text{ in } t_2 : B \oplus D}
78
+
$$
79
+
80
+
In this case:
81
+
82
+
- $t_1$ has the superposition type $A \oplus C$.
83
+
- $t_2$ is well-typed under both contexts $\Gamma, x:A$ and $\Gamma, x:C$, producing types $B$ and $D$.
84
+
- The resulting type of the `let-in` expression is $B \oplus D$, reflecting the dynamic resolution based on the branch of the superposition.
85
+
86
+
#### Function Overloading in Context
87
+
88
+
In **Saki**, overloaded functions may operate on different types, but their behavior and return type must be well-typed in each case. For example, consider the following overloaded function `g`:
89
+
90
+
```scala
91
+
defg(x: Int):String="The number is "+ x.toString
92
+
defg(x: Bool):String=if x then"True"else"False"
93
+
```
94
+
95
+
The type of `g` would be:
96
+
97
+
```scala
98
+
g: (Int->String) ⊕ (Bool->String)
99
+
```
100
+
101
+
When `g` is applied to an integer, it returns a string representing the number; when applied to a boolean, it returns `"True"` or `"False"`.
0 commit comments