Skip to content

Commit 6964ae5

Browse files
author
Joel Abrahams
committed
doc: describe total functions
1 parent afbbb5d commit 6964ae5

1 file changed

Lines changed: 54 additions & 0 deletions

File tree

README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,60 @@ def pure sin(x: Int) -> Int := [
475475
]
476476
```
477477

478+
### 🤚 Total functions (🇻 0.5+)
479+
480+
A function may also be total, which means:
481+
482+
1. It is defined for possible values of its domain
483+
2. It will halt on all such inputs
484+
485+
The second property is interesting, because that would imply that the compiler can prove that an arbitrary function can halt.
486+
To build such a compiler, we would need to solve the halting problem (which is impossible).
487+
Instead, we place heavy restrictions on total functions, enforcing that they are weakly normalizing:
488+
489+
1. We may only call total functions
490+
2. Within the _call tree_ of a function, all arguments to nodes in the tree must be _strictly decreasing_ compared to the first parent of a node which is equal to said node.
491+
3. Potentially non-terminating `while` loops are not allowed
492+
4. For may only be called over collections which implement `SizedIterator`, which is also implemented by the built-in:
493+
- `RangeToInclusive` : `..=b`
494+
- `RangeTo` : `..b`
495+
- `Range` : `a..b`
496+
- `RangeInclusive` : `a..=b`
497+
498+
Take for instance this naive implementation of the Fibbonaci sequence:
499+
500+
```mamba
501+
## fibbonaci, implemented using recursion and not dynamic programming
502+
def total pure fibbonaci(x: PosInt) -> Int := match x {
503+
0 => 0
504+
1 => 1
505+
n => fibbonaci(n - 1) + fibbonaci(n - 2)
506+
}
507+
```
508+
509+
This would, with some subsitution magic, give the following _call tree_:
510+
511+
```
512+
fibbonaci(x)
513+
/ \
514+
fibbonaci(x - 1) fibbonaci(x - 2)
515+
```
516+
517+
Thus, this function has the property of a final function, and we may thus mark it as `total` if we so choose.
518+
The reason why we above state "compared to the first parent of a node which is equal to said node." is that we can have situations where we call other total functions which have recursive calls to self.
519+
This allows us to call other recursive functions without having to strictly decrease the value of the input, but still enfroce that calls to self (and more generally recursive calls to the same function) again are strictly decreasing.
520+
This strictly decreasing property is likely difficult to prove, so we will limit this to a set of primtives at first.
521+
522+
In general:
523+
524+
- If a function is `pure`, it has no side effects.
525+
- If a function is `total`, it will terminate for all possible inputs.
526+
527+
One does not imply the other, so you need both keywords if you want to say a function is total and pure.
528+
529+
The intended use-case is a bit more niche, likely mostly functions in the standard library, to show that they halt on all possible inputs.
530+
But we can imagine that library writers might find these useful if they wish to be more thorough.
531+
478532
### ⚠ Error handling
479533

480534
Unlike Python, Mamba does not have `try` `except` and `finally` (or `try` `catch` as it is sometimes known).

0 commit comments

Comments
 (0)