Skip to content

Commit 589d26d

Browse files
committed
docs: fix render errors
1 parent 93b1480 commit 589d26d

15 files changed

+760
-748
lines changed

docs/Definition/Decorator.md

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## Decorator
1+
# Decorator
22

33
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:
44

@@ -10,7 +10,7 @@ This means it takes a function of type `T -> R` and returns a function of the sa
1010

1111
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.
1212

13-
### Syntax
13+
## Syntax
1414

1515
The syntax for applying one or more decorators to a function is as follows:
1616

@@ -27,7 +27,7 @@ def func: T -> R
2727

2828
- **`#[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.
2929

30-
### Typing Rules
30+
## Typing Rules
3131

3232
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:
3333

@@ -37,61 +37,61 @@ $$
3737

3838
This ensures that the decorator takes in a function of type `T -> R` and returns a function with the same signature.
3939

40-
### Examples of Decorators
40+
## Examples
4141

42-
1. **Ensuring Positive Input Values**
42+
### Ensuring Positive Input Values
4343

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`.
4545

46-
```scala
47-
universe GreaterThan(T: 'Type) = contract {
48-
require (>)(self, other: T): Bool
49-
}
46+
```scala
47+
universe GreaterThan(T: 'Type) = contract {
48+
require (>)(self, other: T): Bool
49+
}
5050

51-
def ensurePositive[T: 'GreaterThan(ℕ)](func: T -> Option[T]): T -> Option[T] = {
52-
return |arg: T|: Option[T] => if arg > 0 then func(arg) else None
53-
}
51+
def ensurePositive[T: 'GreaterThan(ℕ)](func: T -> Option[T]): T -> Option[T] = {
52+
return |arg: T|: Option[T] => if arg > 0 then func(arg) else None
53+
}
5454

55-
#[ensurePositive]
56-
def safeDivide(x: ℕ): Option[ℕ] = if x != 0 then Some(10 / x) else None
57-
```
55+
#[ensurePositive]
56+
def safeDivide(x: ℕ): Option[ℕ] = if x != 0 then Some(10 / x) else None
57+
```
5858

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`.
6161

62-
2. **Transforming Input Before Applying a Function**
62+
### Transforming Input Before Applying a Function
6363

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.
6565

66-
```scala
67-
def mapInput[T1 T2 R: 'Type](transform: T1 -> T2, func: T2 -> R): T1 -> R = {
68-
return |arg: T1|: R => func(transform(arg))
69-
}
66+
```scala
67+
def mapInput[T1 T2 R: 'Type](transform: T1 -> T2, func: T2 -> R): T1 -> R = {
68+
return |arg: T1|: R => func(transform(arg))
69+
}
7070

71-
#[mapInput(|x: ℕ| => x * 2)]
72-
def half(n: ℕ):= n / 2
73-
```
71+
#[mapInput(|x: ℕ| => x * 2)]
72+
def half(n: ℕ):= n / 2
73+
```
7474

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`.
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`.
7777

78-
3. **Transforming the Output of a Function**
78+
### Transforming the Output of a Function
7979

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.
8181

82-
```scala
83-
def mapOutput[T R1 R2: 'Type](func: T -> R1, transform: R1 -> R2): T -> R2 = {
84-
return |arg: T|: R2 => transform(func(arg))
85-
}
86-
87-
#[mapOutput(|x: ℕ| => x * 10)]
88-
def increment(n: ℕ):= n + 1
89-
```
82+
```scala
83+
def mapOutput[T R1 R2: 'Type](func: T -> R1, transform: R1 -> R2): T -> R2 = {
84+
return |arg: T|: R2 => transform(func(arg))
85+
}
86+
87+
#[mapOutput(|x: ℕ| => x * 10)]
88+
def increment(n: ℕ):= n + 1
89+
```
9090

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`.
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`.
9393

94-
### Composition of Decorators
94+
## Composition of Decorators
9595

9696
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.
9797

@@ -108,7 +108,7 @@ In this case:
108108

109109
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`.
110110

111-
#### Contract Universes and Decorators
111+
### Contract Universes and Decorators
112112

113113
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.
114114

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Function Overloading
2+
3+
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+
def f(x: Int): Int = x + 1
53+
def f(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+
def g(x: Int): String = "The number is " + x.toString
92+
def g(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"`.
102+

0 commit comments

Comments
 (0)