diff --git a/doc/md/getting-started/basic-concepts.md b/doc/md/getting-started/basic-concepts.md index 08b572c5722..9fb54735340 100644 --- a/doc/md/getting-started/basic-concepts.md +++ b/doc/md/getting-started/basic-concepts.md @@ -53,6 +53,14 @@ 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. @@ -103,6 +111,12 @@ A declaration list is not itself an expression, so you cannot declare another va **Block expressions** can be formed from a list of declarations by enclosing it with matching curly braces. Blocks are only allowed as sub-expressions of control flow expressions like `if`, `loop`, `case`, etc. A block expression produces a value and, when enclosed in parentheses, can occur within some larger, compound expression. +:::note + +A particular form of blocks are provided for convenience when processing data that may be missing or incomplete. These are described under [option blocks](../writing-motoko/pattern-matching.md#option-blocks-for-streamlined-processing-of-optional-data). + +::: + In all other places, `do { … }` is used to represent block expressions and distinguish blocks from object literals. For example, `do {}` is the empty block of type `()`, while `{}` is an empty record of record type `{}`. This block form preserves the autonomy of the declaration list and its choice of variable names. This means that variables' scopes may nest, but they may not interfere as they nest. Language theorists call this idea **lexical scoping**. Aside from mathematical clarity, the chief practical benefit of lexical scoping is security and its use in building compositionally-secure systems. Specifically, Motoko gives very strong composition properties. For example, nesting your program within a program you do not trust cannot arbitrarily redefine your variables with different meanings. @@ -177,7 +191,7 @@ Because the meaning of execution is ill-defined after a faulting trap, execution :::note -Traps that occur within actor messages are more subtle: they don’t abort the entire actor, but prevent that particular message from proceeding, rolling back any yet uncommitted state changes. Other messages on the actor will continue execution. +Traps that occur within actor messages are more subtle: they don’t abort the entire actor, but prevent that particular message from proceeding, rolling back any yet uncommitted state changes. Other messages on the actor will continue execution. This has subtle security implications, so be sure to consult the relevant [security recommendations](https://internetcomputer.org/docs/current/developer-docs/security/security-best-practices/inter-canister-calls#recommendation). ::: @@ -199,3 +213,19 @@ 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 `` which must appear at the call site. Similarly, functions demanding the `system` capability declare it in their signature: + +``` motoko +func elevate(ref : Int, callback : Int -> async* ()) { + ignore Timer.setTimer(#seconds 0, func() : async () { await* callback ref }) +} +``` diff --git a/doc/md/writing-motoko/pattern-matching.md b/doc/md/writing-motoko/pattern-matching.md index 5a37729a642..b0c123bab65 100644 --- a/doc/md/writing-motoko/pattern-matching.md +++ b/doc/md/writing-motoko/pattern-matching.md @@ -18,7 +18,7 @@ The following table summarizes the different ways of pattern matching. | Named | `age`, `x` | Everywhere | No | Introduces identifiers into a new scope | | Wildcard | `_` | Everywhere | No | | | Typed | `age : Nat` | Everywhere | Conditional | | -| Option | `?0`, `?val` | Everywhere | Yes | | +| Option | `?0`, `?val` | Everywhere | Yes | See also [option blocks](#option-blocks-for-streamlined-processing-of-optional-data) | | Tuple | `( component0, component1, …​ )` | Everywhere | Conditional | Must have at least two components | | Object | `{ fieldA; fieldB; …​ }` | Everywhere | Conditional | Allowed to mention a subset of fields | | Field | `age`, `count = 0` | Object | Conditional | `age` is short for `age = age` | @@ -100,6 +100,22 @@ Some types contain just a single value. We call these singleton types. Examples ### Exhaustiveness (coverage) checking - At runtime, a switch expression may wind up scrutinizing a value to which none of its alternative patterns apply, generating an undesired trap. -To detect the possibility of such runtime failures, the Motoko compiler checks for the exhaustiveness of pattern matching by keeping track of the covered shape of the scrutinee. The compiler issues a warning for any non-covered scrutinees. Motoko even constructs a helpful example of a scrutinee that is not matched. A useful by-product of the exhaustiveness check is that it identifies and warns about dead or redundant alternatives that can never be matched. \ No newline at end of file +To detect the possibility of such runtime failures, the Motoko compiler checks for the exhaustiveness of pattern matching by keeping track of the covered shape of the scrutinee. The compiler issues a warning for any non-covered scrutinees. Motoko even constructs a helpful example of a scrutinee that is not matched. A useful by-product of the exhaustiveness check is that it identifies and warns the developer about dead or redundant alternatives that can never be matched. + +## 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: + +``` motoko +let true = isLoggedIn(customer) else return; +// process message for logged-in customer +``` + +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. + +## 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`.