Skip to content

Commit

Permalink
Merge branch 'master' into ryan/component-call
Browse files Browse the repository at this point in the history
  • Loading branch information
rvanasa authored Jul 22, 2024
2 parents 3912581 + 0911764 commit 93fcb8b
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 31 deletions.
24 changes: 0 additions & 24 deletions doc/md/getting-started/basic-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,6 @@ Motoko permits user-defined types and each of the following non-primitive value

For precise language definitions of primitive and non-primitive values, see the [language reference](../reference/language-manual).

### Objects, records and their extension mechanisms

Objects are aggregate data made from *labeled* constituent data. In their most general form, objects can contain named values (`let` and `var`) as well as methods (`func`) that act on them. Objects are written with the leading keyword `object` followed by an optional name and the block comprising its constituents. Only `public` constituents contribute to the object's type.

In many cases, objects are used as simple containers of data, which are referred to as *records*. When building records, Motoko has a simplified syntax to offer where semicolon-separated named fields are placed in braces. The labels are identifiers (with a leading `var` when the field is mutable), followed by `=` and the initial value. All fields are public and contribute to the record's type.

Furthermore, syntactic forms are provided for building new records from existing ones, adding new fields, or replacing existing ones. The *base* records and objects are separated by the `and` keyword and can be followed by `with` and semicolon-separated additional (or overwriting) fields. The bases and fields are wrapped in braces, indicating record formation. When the bases have overlapping fields (considering their types), then a disambiguating field overwrite must be provided. The original bases remain unmodified, and thus we refer to this as a functional record combination and extension.

## Printing values

The function `print`, from base library [`Debug`](../base/Debug.md), accepts a text string of type [`Text`](../base/Text.md) as input, and produces the unit value of unit type or `()`, as its output.
Expand Down Expand Up @@ -213,19 +205,3 @@ assert n % 2 == 0; // traps when n not even
```

Because an assertion may succeed, and thus proceed with execution, it may only be used in context where a value of type `()` is expected.

### The `system` capability

Smart contracts (i.e., Motoko `actor`s) may generally contain assets that correspond to real-world value, the loss of which can be detrimental. Many such assets are mobile and can be transferred through message sending. In order for a library to be able to send a message, the corresponding function must be imported and possess an `async` (or `async*`) type. Calling such functions is only possible when the send capability is given by the callee, also having such a type.

Naturally, the programmer must be careful when granting send privileges to third-party libraries (resp. functions therein) and reviewing for potential security breaches if they choose to do so.

There is a special class of functions, however, that can be called from code that does not possess send capability but registers a callback that does. This kind of *capability elevation* is possible when adding timers (e.g. `setTimer` in `base`). To harden the `actor` against supply-chain attacks (malicious sends masquerading behind a capability-starved call interface to third-party code), Motoko allows to declare functions that can potentially lead to capability elevation to require a `system` capability.

The `system` capability originates from the top-level actor and can be passed to functions that expect it by specifying a pseudo-type parameter `<system, ...>` which must appear at the call site. Similarly, functions demanding the `system` capability declare it in their signature:

``` motoko
func elevate<system>(ref : Int, callback : Int -> async* ()) {
ignore Timer.setTimer<system>(#seconds 0, func() : async () { await* callback ref })
}
```
2 changes: 1 addition & 1 deletion doc/md/writing-motoko/control-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Labeled common expressions don’t allow `continue`. In terms of typing, both `<

## Option blocks and null breaks

Motoko lets you opt in to `null` values, tracking possible occurrences of `null` values using option types of the form `?T`. This is to both to encourage you to avoid using `null` values when possible, and to consider the possibility of `null` values when necessary. Motoko simplifies the handling of option types with some dedicated syntax: option blocks and null breaks.
Motoko lets you opt in to `null` values, tracking possible occurrences of `null` values using option types of the form `?T`. This is both to encourage you to avoid using `null` values when possible, and to consider the possibility of `null` values when necessary. Motoko simplifies the handling of option types with some dedicated syntax: option blocks and null breaks.

The option block, `do ? <block>`, produces a value of type `?T`, when block `<block>` has type `T` and, importantly, introduces the possibility of a break from `<block>`. Within a `do ? <block>`, the null break `<exp> !`, tests whether the result of the expression, `<exp>`, of unrelated option type, `?U`, is `null`. If the result `<exp>` is `null`, control immediately exits the `do ? <block>` with value `null`. Otherwise, the result of `<exp>` must be an option value `?v`, and evaluation of `<exp> !` proceeds with its contents, `v` of type `U`.

Expand Down
52 changes: 52 additions & 0 deletions doc/md/writing-motoko/local-objects-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,55 @@ func Bits(n : Nat) : Bits = object {
```


## Object combination and extension

Motoko allows you to construct a single object from a simple record and a more complicated object block, while also providing syntax for building new objects from existing ones, adding new fields, or replacing existing fields.
The *base* records and objects are separated by the `and` keyword and can be followed by `with` and semicolon-separated additional (or overriden) fields.
The bases and fields are enclosed in braces, indicating record formation.
When the bases have overlapping fields (according to their types), then a disambiguating field overwrite must be provided.
The original bases are never modified; instead, their fields are copied to create a new object, and thus we refer to this as a functional object combination and extension.

Here are some simple examples:

1. Object combination with `and`:
The `and` keyword combines two or more objects.

``` motoko
let person = { name = "Alice"; };
let employee = { id = 123; department = "Engineering" };
let employedPerson = { person and employee };
// employeePerson now has: name, id, and department
```

2. Object extension with `with`:
The `with` keyword allows you to add new fields or override existing ones.

``` motoko
let person = { name = "Alice" };
let agedPerson = { person with age = 30 };
// agedPersion now has: name and age
```

3. Combining `and` and `with`:
You can use both `and` and `with` together for more complex object manipulations.

``` motoko
let person = { name = "Alice" };
let employee = { id = 123; department = "Engineering" };
let employedPersonWithAge = { person and employee with age = 30 };
// employedPersionWithAge now has: name, id, department and age
```

Key points to remember:
- When using `and`, if there are conflicting field names in the bases, the conflict must be resolved using a `with` field.
- The `with` clause is used to disambiguate field labels, define new fields, override existing fields, add new `var` fields, or redefine existing `var` fields to prevent aliasing.
- You must explicitly override any `var` fields from base objects to prevent introducing aliases.

This syntax provides a convenient way to create modular and reusable code in Motoko, allowing developers to build complex objects from simpler components and
extend existing objects with new functionality.

For more details, see the [language manual](../reference/language-manual#object-combinationextension).
24 changes: 18 additions & 6 deletions doc/md/writing-motoko/pattern-matching.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,29 @@ To detect the possibility of such runtime failures, the Motoko compiler checks f

## Refutable patterns and dealing with non-matching data

Developers may only be interested in specially formed data with a desire to directly handle all non-matching forms. The `let`-`else` construct is designed precisely for this purpose. Whereas the regular destructuring `let` allows to focus on a single given pattern, it invariably traps if the right-hand side data doesn't match it, which is warned at compilation. The `else` clause gives the programmer a way to deal with refuted matches, such as bailing out of the process or logging a message before trapping. As such, `let`-`else` is similar to a two-`case` `switch` in a compact form that additionally doesn't force the indentation of the processing logic following it.

The below example illustrates how you can write a non-indenting `if`-`else` by resorting to a `let`-`else` in your code:
The `let`-`else` construct in Motoko is designed for developers who want to work with a specific pattern of data while handling all non-matching data on a different control flow path. Unlike the standard destructuring `let`, which traps (and triggers a compile-time warning) when the data doesn't match the expected pattern, `let`-`else` provides a way to manage refuted matches. This construct allows programmers to gracefully handle mismatches, such as exiting the current function or logging a message before trapping.

`let`-`else` can be seen as a more compact version of a two-case `switch` statement. It has the added benefit of not requiring indentation for the code that follows it, which can improve readability. This feature enables developers to write non-indenting `if`-`else`-like structures in their code.

Here's an example demonstrating how to use `let`-`else` to avoid a less readable, indentation-increasing `switch`:

``` motoko
let true = isLoggedIn(customer) else return;
// process message for logged-in customer
func getName(optionalName : ?Text) : Text {
let ?name = optionalName else return "Unknown";
name
}
```

The expression (or block) following the `else` must be of type `None` signifying that its execution trajectory mustn't contribute to the code immediately following the `let` declaration.
In a `let-else` construct, the expression or block following the `else` keyword must have type `None`. This indicates that its execution cannot enter the code following the `let` declaration but must change the flow of control, typically by returning early, breaking to some enclosing label or trapping.

## Option blocks for streamlined processing of optional data

Pattern matching on optional data (of type `?T`) is a preferred technique for avoiding the dreaded `null`-exeption problems known from other languages. However, `switch`-ing  on several options can lead to tedious coding and deeply nested sources. To remedy these problems, Motoko provides *option blocks* (`do ? { ... }`) that allow safe unwrapping of options using a postfix `!` operator. Every use of the `!` in the block corresponds to a `switch` on some option, with the additional short-circuiting behavior that if `!` is applied to a `null` value, the entire block stops evaluation and immediately returns `null`.
Motoko offers a preferred method for handling optional data (of type `?T`) through pattern matching, which helps avoid the notorious `null`-exception issues common in other programming languages.
However, using multiple switch statements on several options can become cumbersome and result in deeply nested, hard-to-read code.
To address this, Motoko introduces a feature called *option blocks*, written as `do ? { ... }`.
These blocks allow for safe unwrapping of optional values using a postfix `!` operator.
Each use of `!` within the block is equivalent to a switch statement on an option, but with an added benefit: if `!` is applied to a `null` value, the entire block immediately abandons execution and returns `null`.
This short-circuiting behavior simplifies the handling of multiple optional values in a more concise and readable manner.

For an example, see [option blocks and null breaks](control-flow#option-blocks-and-null-breaks).
68 changes: 68 additions & 0 deletions doc/md/writing-motoko/system-capability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
sidebar_position: 25
---

# System capability

The `system` capability in Motoko is used to control access to sensitive system functions and prevent potential misuse of these functions.
It serves as a safety mechanism to ensure that developers are explicitly aware when they are using or granting access to powerful system-level operations.

Specifically, the system capability is required for calling sensitive functions such as:

1. `ExperimentalCycles.add`: This function is used for cycle management, which is crucial for controlling the computational resources on the Internet Computer.

2. `Timer.setTimer`: This function is used for scheduling future computations, which can have significant impacts on system resources and behavior.

The introduction of the system capability helps address a security concern where, in previous versions of Motoko, third-party library functions could make silent calls
to these sensitive functions without providing any indication to the caller.
This could potentially lead to unexpected behavior or resource usage.

By requiring explicit declaration and passing of the system capability, Motoko now ensures that developers are fully aware when they are using or allowing the use of these powerful system functions.
This helps prevent accidental misuse and makes the code's intentions clearer, enhancing both security and code readability.

It's important to note that while the system capability allows the use of these sensitive functions, it doesn't automatically grant unlimited access to all system resources.
It's a type-level mechanism to control and make explicit the use of specific system functions, rather than a comprehensive permission system.

The system capability was introduced in Motoko version 0.11.0.
The key change is the introduction of the `system` pseudo-type parameter and argument.
For instance, `ExperimentalCycles.add` has been revised from having type `Nat -> ()` to having type `<system>Nat -> ()`,
reflecting the new system capability requirement.

To use system capabilities in your code, you must now explicitly declare them when required,
and should explicitly supply them when granting permission to a callee.

Functions that need to use system capabilities must include the `system` pseudo-type parameter.
It's important to note that `system`, if specified, must be the first parameter in function or class declarations.

For example:

``` motoko no-repl
func splitCycles<system>() {
let amount = ExperimentalCycles.balance() / 2;
ExperimentalCycles.add(amount); // generates a warning
}
```

To suppress warnings about implicit system capability usage, you can
explicitly pass the `system` capability at call sites too:

``` motoko no-repl
func splitCycles<system>() {
let amount = ExperimentalCycles.balance() / 2;
ExperimentalCycles.add<system>(amount); // accepted without warning
}
```

System capabilities are available in specific contexts, such as within
actor expression or actor class bodies, non-query shared functions,
asynchronous functions, async expressions, local functions or classes
declared with the `system` pseudo-type parameter, and the preupgrade
and postupgrade system functions.

However, they are not available in query methods, composite query
methods, or functions that don't declare their need of system capability.

For more details, see the [language manual](../reference/language-manual#type-arguments).

For details on migrating code from earlier Motoko versions, see this [guide](../migration-guides/0.11.0-migration-guide).

0 comments on commit 93fcb8b

Please sign in to comment.