Skip to content

Commit

Permalink
Merge branch 'master' into gabor/parentheticals
Browse files Browse the repository at this point in the history
  • Loading branch information
ggreif committed Dec 6, 2024
2 parents 64696a1 + 76f9087 commit 57e213b
Show file tree
Hide file tree
Showing 119 changed files with 1,226 additions and 621 deletions.
48 changes: 48 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,53 @@
# Motoko compiler changelog

## 0.13.5 (2024-12-06)

* motoko (`moc`)

* Breaking change (minor):

* Add new keyword `transient` with exactly the same meaning as the old keyword `flexible` (but a more familiar reading).

* Add keyword `persistent`.

When used to modify the `actor` keyword in an actor or actor class definition, the keyword declares that the default stability of a
`let` or `var` declaration is `stable` (not `flexible` or `transient`).

For example, a stateful counter can now be declared as:

``` motoko
persistent actor {
// counts increments since last upgrade
transient var invocations = 0;
// counts increments since first installation
var value = 0; // implicitly `stable`
public func inc() : async () {
value += 1;
invocations += 1;
}
}
```
On upgrade, the transient variable `invocations` will be reset to `0` and `value`, now implicitly `stable`, will retain its current value.
Legacy actors and classes declared without the `persistent` keyword have the same semantics as before.
* Added new primitive `replyDeadline : () -> Nat64` to obtain when a response for a best-effort message is due (#4795).
* bugfix: fail-fast by limiting subtyping depth to avoid reliance on unpredictable stack overflow (#3057, #4798).
* motoko-base
* Added `Text.fromList` and `Text.toList` functions (dfinity/motoko-base#676).
* Added `Text.fromArray/fromVarArray` functions (dfinity/motoko-base#674).
* Added `replyDeadline` to `ExperimentalInternetComputer` (dfinity/motoko-base⁠#677).
## 0.13.4 (2024-11-29)
* motoko (`moc`)
Expand Down
2 changes: 1 addition & 1 deletion doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ html:
preview:
make -C ../src moc_interpreter.js
cp -f ../src/moc_interpreter.js docusaurus/static
cd docusaurus; npm install; npm start
cd docusaurus; npm install; npm run clear; npm start


.PHONY: base
Expand Down
6 changes: 3 additions & 3 deletions doc/docusaurus/src/theme/CodeBlock/hljs_run.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ export function registerMotoko() {
$pattern: "[a-zA-Z_]\\w*",
keyword:
"actor and await break case catch class" +
" continue composite debug do else for func if in import" +
" module not object or label let loop private" +
" public return shared try throw query switch" +
" continue composite debug do else finally for func if in import" +
" module not object or label let loop persistent private" +
" public return shared transient try throw query switch" +
" type var while with stable flexible system debug_show assert ignore from_candid to_candid",
literal: "true false null",
built_in:
Expand Down
2 changes: 1 addition & 1 deletion doc/md/base/OrderedMap.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Credits:
The core of this implementation is derived from:

* Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on:
* Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html).
* Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](https://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html).

## Type `Map`
``` motoko no-repl
Expand Down
2 changes: 1 addition & 1 deletion doc/md/base/OrderedSet.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Credits:
The core of this implementation is derived from:

* Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on:
* Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html).
* Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](https://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html).

## Type `Set`
``` motoko no-repl
Expand Down
2 changes: 1 addition & 1 deletion doc/md/base/RBTree.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Credits:
The core of this implementation is derived from:

* Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on:
* Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html).
* Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](https://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html).

## Type `Color`
``` motoko no-repl
Expand Down
22 changes: 20 additions & 2 deletions doc/md/base/Timer.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
# Timer
Timers for one-off or periodic tasks.

Timers for one-off or periodic tasks. Applicable as part of the default mechanism.

Note: If `moc` is invoked with `-no-timer`, the importing will fail.

Note: The resolution of the timers is in the order of the block rate,
so durations should be chosen well above that. For frequent
canister wake-ups the heatbeat mechanism should be considered.
canister wake-ups the heartbeat mechanism should be considered.

Note: The functionality described below is enabled only when the actor does not override it by declaring an explicit `system func timer`.

Note: Timers are _not_ persisted across upgrades. One possible strategy
to re-establish timers after an upgrade is to walk stable variables
in the `post_upgrade` hook and distill necessary timer information
from there.

Note: Basing security (e.g. access control) on timers is almost always
the wrong choice. Be sure to inform yourself about state-of-the art
dApp security. If you _must use_ timers for security controls, be sure
to consider reentrancy issues, and the vanishing of timers on upgrades
and reinstalls.

Note: For further usage information for timers on the IC please consult
https://internetcomputer.org/docs/current/developer-docs/backend/periodic-tasks#timers-library-limitations

## Type `Duration`
``` motoko no-repl
Expand Down
36 changes: 23 additions & 13 deletions doc/md/canister-maintenance/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,25 @@ The following is a simple example of how to declare a stateful counter:
```

Importantly, in this example, when the counter is upgraded, its state is lost.
This is because actor variables are by default `flexible`, meaning they get reinitialized on an upgrade.
This is because actor variables are by default `transient`, meaning they get reinitialized on an upgrade.
The above actor is equivalent to using the `transient` declaration:

``` motoko no-repl file=../examples/count-v0transient.mo
```

To fix this, you can declare a stable variable that is retained across upgrades:

To fix this, you can declare a `stable` variable that is retained across upgrades:


``` motoko no-repl file=../examples/count-v1stable.mo
```

To make `stable` the default for all declarations and `transient` optional, you can prefix the actor declaration with the keyword `persistent`.

``` motoko no-repl file=../examples/count-v1.mo
```

If the variable `state` were not declared `stable`, `state` would restart from `0` on upgrade.
If the variable `state` were not declared `stable`, either explicitly or by applying `persistent` to the `actor` keyword, `state` would restart from `0` on upgrade.

## Evolving the stable declarations

Expand Down Expand Up @@ -188,23 +198,23 @@ A common, real-world example of an incompatible upgrade can be found [on the for
In that example, a user was attempting to add a field to the record payload of an array, by upgrading from stable type interface:
``` motoko no-repl
actor {
persistent actor {
type Card = {
title : Text;
};
stable var map : [(Nat32, Card)] = [(0, { title = "TEST"})];
var map : [(Nat32, Card)] = [(0, { title = "TEST"})];
};
```

to *incompatible* stable type interface:

``` motoko no-repl
actor {
persistent actor {
type Card = {
title : Text;
description : Text;
};
stable var map : [(Nat32, Card)] = [];
var map : [(Nat32, Card)] = [];
};
```

Expand Down Expand Up @@ -239,17 +249,17 @@ To resolve this issue, an [explicit](#explicit-migration) is needed:
``` motoko no-repl
import Array "mo:base/Array";
actor {
persistent actor {
type OldCard = {
title : Text;
};
type NewCard = {
title : Text;
description : Text;
};
stable var map : [(Nat32, OldCard)] = [];
stable var newMap : [(Nat32, NewCard)] = Array.map<(Nat32, OldCard), (Nat32, NewCard)>(
var map : [(Nat32, OldCard)] = [];
var newMap : [(Nat32, NewCard)] = Array.map<(Nat32, OldCard), (Nat32, NewCard)>(
map,
func(key, { title }) { (key, { title; description = "<empty>" }) },
);
Expand All @@ -259,12 +269,12 @@ actor {
4. **After** we have successfully upgraded to this new version, we can upgrade once more to a version, that drops the old `map`.

``` motoko no-repl
actor {
persistent actor {
type Card = {
title : Text;
description : Text;
};
stable var newMap : [(Nat32, Card)] = [];
var newMap : [(Nat32, Card)] = [];
};
```

Expand Down
55 changes: 28 additions & 27 deletions doc/md/canister-maintenance/upgrades.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,44 @@ This is substantially different to other languages supported on the IC, which us

In an actor, you can configure which part of the program is considered to be persistent, i.e. survives upgrades, and which part are ephemeral, i.e. are reset on upgrades.

More precisely, each `let` and `var` variable declaration in an actor can specify whether the variable is `stable` or `flexible`. If you don’t provide a modifier, the variable is assumed to be `flexible` by default.
More precisely, each `let` and `var` variable declaration in an actor can specify whether the variable is `stable` or `transient`. If you don’t provide a modifier, the variable is assumed to be `transient` by default.


The semantics of the modifiers is as follows:
* `stable` means that all values directly or indirectly reachable from that stable actor variable are considered persistent and automatically retained across upgrades. This is the primary choice for most of the program's state.
* `flexible` means that the variable is re-initialized on upgrade, such that the values referenced by this flexible variable can be discarded, unless the values are transitively reachable by other variables that are stable. `flexible` is only used for temporal state or references to high-order types, such as local function references, see [stable types](#stable-types).
* `transient` means that the variable is re-initialized on upgrade, such that the values referenced by this transient variable can be discarded, unless the values are transitively reachable by other variables that are stable. `transient` is only used for temporary state or references to high-order types, such as local function references, see [stable types](#stable-types).

:::note

Previous versions of Motoko (up to version 0.13.4) used the keyword `flexible` instead of `transient`. Both keywords are accepted interchangeably but the legacy `flexible` keyword may be deprecated in the future.

:::note

The following is a simple example of how to declare a stable counter that can be upgraded while preserving the counter’s value:

``` motoko file=../examples/StableCounter.mo
```

Starting with Motoko v0.13.5, if you prefix the `actor` keyword with the keyword `persistent`, then all `let` and `var` declarations of the actor or actor class are implicitly declared `stable`. Only `transient` variables will need an explicit `transient` declaration.
Using a `persistent` actor can help avoid unintended data loss. It is the recommended declaration syntax for actors and actor classes. The non-`persistent` declaration is provided for backwards compatibility.

Since Motoko v0.13.5, the recommended way to declare `StableCounter` above is:

``` motoko file=../examples/PersistentCounter.mo
```

:::note

You can only use the `stable` or `flexible` modifier on `let` and `var` declarations that are **actor fields**. You cannot use these modifiers anywhere else in your program.
You can only use the `stable`, `transient` (or legacy `flexible`) modifier on `let` and `var` declarations that are **actor fields**. You cannot use these modifiers anywhere else in your program.

:::

When you first compile and deploy a canister, all flexible and stable variables in the actor are initialized in sequence. When you deploy a canister using the `upgrade` mode, all stable variables that existed in the previous version of the actor are pre-initialized with their old values. After the stable variables are initialized with their previous values, the remaining flexible and newly-added stable variables are initialized in sequence.

When you first compile and deploy a canister, all transient and stable variables in the actor are initialized in sequence. When you deploy a canister using the `upgrade` mode, all stable variables that existed in the previous version of the actor are pre-initialized with their old values. After the stable variables are initialized with their previous values, the remaining transient and newly-added stable variables are initialized in sequence.

:::danger
Do not forget to declare variables `stable` if they should survive canister upgrades as the default is `flexible` if no modifier is declared.
Do not forget to declare variables `stable` if they should survive canister upgrades as the default is `transient` if no modifier is declared.
A simple precaution is declare the entire actor or actor class `persistent`.
:::

## Persistence modes
Expand Down Expand Up @@ -67,31 +84,15 @@ For variables that do not have a stable type, there are two options for making t
Unlike stable data structures in the Rust CDK, these modules do not use stable memory but rely on orthogonal persistence. The adjective "stable" only denotes a stable type in Motoko.
:::

2. Extract the state in a stable type, and wrap it in the non-stable type.
2. Extract the state in a stable type, and wrap it in the non-stable type.

For example, the stable type `TemperatureSeries` covers the persistent data, while the non-stable type `Weather` wraps this with additional methods (local function types).

```motoko no-repl
actor {
type TemperatureSeries = [Float];
class Weather(temperatures : TemperatureSeries) {
public func averageTemperature() : Float {
var sum = 0.0;
var count = 0.0;
for (value in temperatures.vals()) {
sum += value;
count += 1;
};
return sum / count;
};
};
stable var temperatures : TemperatureSeries = [30.0, 31.5, 29.2];
flexible var weather = Weather(temperatures);
};

``` motoko no-repl file=../examples/WeatherActor.mo
```


3. __Discouraged and not recommended__: [Pre- and post-upgrade hooks](#preupgrade-and-postupgrade-system-methods) allow copying non-stable types to stable types during upgrades. This approach is error-prone and does not scale for large data. **Per best practices, using these methods should be avoided if possible.** Conceptually, it also does not align well with the idea of orthogonal persistence.

## Stable type signatures
Expand Down Expand Up @@ -121,7 +122,7 @@ A stable signature `<stab-sig1>` is stable-compatible with signature `<stab-sig2
- `<stab-sig2>` does not contain a stable field `<id>`.
- `<stab-sig>` has a matching stable field `<id> : U` with `T <: U`.

Note that `<stab-sig2>` may contain additional fields or abandon fields of `<stab-sig1>`. Mutability can be different for matching fields.
Note that `<stab-sig2>` may contain additional fields or abandon fields of `<stab-sig1>`. Mutability can be different for matching fields.

`<stab-sig1>` is the signature of an older version while `<stab-sig2>` is the signature of a newer version.

Expand All @@ -142,7 +143,7 @@ When upgrading a canister, it is important to verify that the upgrade can procee
- Breaking clients due to a Candid interface change.

With [enhanced orthogonal persistence](orthogonal-persistence/enhanced.md), Motoko rejects incompatible changes of stable declarations during upgrade attempt.
Moreover, `dfx` checks the two conditions before attempting then upgrade and warns users correspondingly.
Moreover, `dfx` checks the two conditions before attempting the upgrade and warns users correspondingly.

A Motoko canister upgrade is safe provided:

Expand Down
2 changes: 1 addition & 1 deletion doc/md/examples/Alarm.mo
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Debug "mo:base/Debug";

actor Alarm {
persistent actor Alarm {

let n = 5;
var count = 0;
Expand Down
12 changes: 7 additions & 5 deletions doc/md/examples/Buckets.mo
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import Nat "mo:base/Nat";
import Map "mo:base/RBTree";
import Map "mo:base/OrderedMap";

actor class Bucket(n : Nat, i : Nat) {
persistent actor class Bucket(n : Nat, i : Nat) {

type Key = Nat;
type Value = Text;

let map = Map.RBTree<Key, Value>(Nat.compare);
transient let keyMap = Map.Make<Key>(Nat.compare);

var map : Map.Map<Key, Value> = keyMap.empty();

public func get(k : Key) : async ?Value {
assert((k % n) == i);
map.get(k);
keyMap.get(map, k);
};

public func put(k : Key, v : Value) : async () {
assert((k % n) == i);
map.put(k,v);
map := keyMap.put(map, k, v);
};

};
Loading

0 comments on commit 57e213b

Please sign in to comment.