diff --git a/docs/build-on-btc/btc-dev-env.mdx b/docs/build-on-btc/btc-dev-env.mdx index 02d52878a6..aa1ea4381e 100644 --- a/docs/build-on-btc/btc-dev-env.mdx +++ b/docs/build-on-btc/btc-dev-env.mdx @@ -17,7 +17,7 @@ To develop Bitcoin applications to be deployed on ICP, your local developer envi - The Rust toolchain for installing Rust packages and compiling Rust code. - - The Motoko compiler and base library for writing and compiling Motoko code. + - The Motoko compiler and standard library (core) for writing and compiling Motoko code. - The IC SDK for creating, deploying, and managing smart contracts. @@ -36,9 +36,9 @@ Before developing BTC applications in Rust, you will need to install the Rust to - [The Rust programming language.](https://www.rust-lang.org/tools/install) - [The `cargo` package manager.](https://doc.rust-lang.org/cargo/getting-started/installation.html) -### Motoko compiler and base library +### Motoko compiler and standard library (core) -The Motoko compiler (`moc`) and base library are included in the IC SDK installation as described below. Alternatively, you can install them on their own through the [Motoko release binaries](https://github.com/dfinity/motoko/releases). +The Motoko compiler (`moc`) and the core library (the standard library for Motoko) are included in the IC SDK installation as described below. Alternatively, you can install them on their own through the [Motoko release binaries](https://github.com/dfinity/motoko/releases). ### IC SDK diff --git a/docs/building-apps/canister-management/topping-up.mdx b/docs/building-apps/canister-management/topping-up.mdx index aac052bca0..af68392dc5 100644 --- a/docs/building-apps/canister-management/topping-up.mdx +++ b/docs/building-apps/canister-management/topping-up.mdx @@ -71,7 +71,7 @@ Canisters can check their balance programmatically. -Motoko canisters can use the [ExperimentalCycles library](/motoko/base/ExperimentalCycles) to interact with cycles. +Motoko canisters can use the [Cycles library](/motoko/core/Cycles) to interact with cycles. diff --git a/docs/building-apps/essentials/gas-cost.mdx b/docs/building-apps/essentials/gas-cost.mdx index d960f66b5d..d4bde76ea0 100644 --- a/docs/building-apps/essentials/gas-cost.mdx +++ b/docs/building-apps/essentials/gas-cost.mdx @@ -184,5 +184,5 @@ Common errors related to cycles include: - [Cycles management services](/building-apps/canister-management/topping-up#using-a-cycles-management-service). ### Counting instructions -- [Motoko function `countInstructions`](/motoko/base/ExperimentalInternetComputer#function-countinstructions). +- [Motoko function `countInstructions`](/motoko/core/InternetComputer#function-countinstructions). - [IC interface specification](/references/ic-interface-spec#system-api-performance-counter). diff --git a/docs/building-apps/network-features/randomness.mdx b/docs/building-apps/network-features/randomness.mdx index d3e961917b..f17bd9ea7c 100644 --- a/docs/building-apps/network-features/randomness.mdx +++ b/docs/building-apps/network-features/randomness.mdx @@ -13,7 +13,7 @@ import { BetaChip } from "/src/components/Chip/BetaChip"; -The Internet Computer provides a secure and verifiable way to generate random numbers directly within canisters. This functionality is exposed through the [raw_rand](/references/ic-interface-spec#ic-raw_rand) method offered by the [management canister](/references/system-canisters/management-canister) and the [Motoko Random module](/motoko/base/Random). +The Internet Computer provides a secure and verifiable way to generate random numbers directly within canisters. This functionality is exposed through the [raw_rand](/references/ic-interface-spec#ic-raw_rand) method offered by the [management canister](/references/system-canisters/management-canister) and the [Motoko Random module](/motoko/core/Random). The method takes no input and returns 32 pseudo-random bytes to the caller. @@ -44,7 +44,7 @@ actor { }; ``` -In addition to the `raw_rand` method, Motoko offers a [Random module](/motoko/base/Random) for generating random numbers. +In addition to the `raw_rand` method, Motoko offers a [Random module](/motoko/core/Random) for generating random numbers. diff --git a/docs/building-apps/network-features/time-and-timestamps.mdx b/docs/building-apps/network-features/time-and-timestamps.mdx index 363733df9d..def1c9949b 100644 --- a/docs/building-apps/network-features/time-and-timestamps.mdx +++ b/docs/building-apps/network-features/time-and-timestamps.mdx @@ -133,11 +133,11 @@ fn to_date(timestamp: &u64) -> OffsetDateTime { For some applications, it may be useful to calculate the time that has passed between two timestamps. Below is an example canister written in Motoko that demonstrates how to use [timers](periodic-tasks-timers.mdx) to generate two timestamps, then creates a function that can be used to calculate the difference: ```motoko no-repl -import Debug "mo:base/Debug"; -import { setTimer; recurringTimer } = "mo:base/Timer"; +import Debug "mo:core/Debug"; +import { setTimer; recurringTimer } = "mo:core/Timer"; import Time "mo:time/time"; import DateTime "mo:datetime/DateTime"; -import Int "mo:base/Int"; +import Int "mo:core/Int"; actor Alarm { diff --git a/docs/building-apps/security/canister-upgrades.mdx b/docs/building-apps/security/canister-upgrades.mdx index 81b33a71bf..1f1bdb990c 100644 --- a/docs/building-apps/security/canister-upgrades.mdx +++ b/docs/building-apps/security/canister-upgrades.mdx @@ -48,6 +48,6 @@ Using the Rust CDK, the recurring timer is also lost on upgrade as explained in - In Motoko canisters, global timers should be set up in the actor initializer for canister installation or reinstallation. Canister-wide timers should be set in the `post_upgrade` hook for upgrades, as timers do not survive upgrades and must be explicitly set up thereafter. -- See the Motoko documentation on [recurringTimer](/motoko/base/Timer#function-recurringtimer). +- See the Motoko documentation on [recurringTimer](/motoko/core/Timer#function-recurringtimer). - See the Rust documentation on [set_timer_interval](https://docs.rs/ic-cdk/0.6.9/ic_cdk/timer/fn.set_timer_interval.html). \ No newline at end of file diff --git a/docs/building-apps/test/overview.mdx b/docs/building-apps/test/overview.mdx index e7fa66f2bd..574eae7dae 100644 --- a/docs/building-apps/test/overview.mdx +++ b/docs/building-apps/test/overview.mdx @@ -40,7 +40,7 @@ There are three types of testing: Unit testing refers to testing a single function or component of a piece of code by itself, thus testing a single *unit* of the code. Unit testing does not take into consideration how the single unit functions with other components of the application. The scope of unit testing is more narrow compared to other types of testing and is designed to catch bugs or errors that may occur for each individual unit of the application's code. -In Motoko, the [base library](https://github.com/dfinity/motoko-base/tree/master/test) contains files that can be used for unit testing. Most testing files utilize the Motoko Matchers test library to provide testing functionality. Motoko Matchers also includes a package for executing unit tests within canisters. +In Motoko, the [core library](https://github.com/caffeinelabs/motoko-core) (the recommended standard library) contains modules and patterns used in unit testing. Many projects also use the Motoko Matchers test library for testing functionality. Motoko Matchers includes a package for executing unit tests within canisters. For tests that use large datasets or have long-running test batches, Motoko also provides the [BigTest library](https://github.com/matthewhammer/motoko-bigtest). diff --git a/docs/references/execution-errors.mdx b/docs/references/execution-errors.mdx index 188e48a2ba..04f6413b98 100644 --- a/docs/references/execution-errors.mdx +++ b/docs/references/execution-errors.mdx @@ -253,7 +253,7 @@ An example of this error is:   Canister violated contract: Size of method_name 22000 exceeds the allowed limit of 20000. ``` -Method names are generally short (similar to function names), so it is likely a bug in the calling canister that creates a name that is too long. Consider running the canister locally and using debug printing ([using `Debug` in Motoko](/motoko/base/Debug/) or [`println!` in Rust](https://docs.rs/ic-cdk/latest/ic_cdk/macro.println.html)) to verify that the correct method name is being called. +Method names are generally short (similar to function names), so it is likely a bug in the calling canister that creates a name that is too long. Consider running the canister locally and using debug printing ([using `Debug` in Motoko](/motoko/core/Debug/) or [`println!` in Rust](https://docs.rs/ic-cdk/latest/ic_cdk/macro.println.html)) to verify that the correct method name is being called. ### Canister made a call with too large a payload diff --git a/docs/tutorials/developer-liftoff/level-1/1.1-motoko-lvl1.mdx b/docs/tutorials/developer-liftoff/level-1/1.1-motoko-lvl1.mdx index 381e49216f..11c2ab43f1 100644 --- a/docs/tutorials/developer-liftoff/level-1/1.1-motoko-lvl1.mdx +++ b/docs/tutorials/developer-liftoff/level-1/1.1-motoko-lvl1.mdx @@ -61,20 +61,22 @@ let z = do { The variable `z` now stores the result value of `3` and can be called by additional methods. -Next, let's look at using and importing the base Motoko library. +Next, let's look at using and importing the Motoko standard library. -## Using the base library +## Using the core library -Motoko is designed to minimize built-in types and operations, so whenever possible, the Motoko base library provides the necessary operations and types for developers to utilize. +The recommended standard library for Motoko is **core** ([mops.one/core](https://mops.one/core), [changelog](https://github.com/caffeinelabs/motoko-core/blob/main/Changelog.md)). The older **base** library is still supported; see the [base-to-core migration guide](https://docs.internetcomputer.org/motoko/base-core-migration) when moving to core. -The base library includes a selection of modules that focus on the core features of Motoko. The base library is still under development and is expected to grow in size as additional features are developed. +Motoko is designed to minimize built-in types and operations, so whenever possible, the Motoko core library provides the necessary operations and types for developers to utilize. -To import the base library, the `import` keyword can be used at the start of your Motoko code file. After the `import` keyword, you need to provide a **local module name** and a **file path** that the import declaration can use to locate the imported module. +The core library includes a selection of modules that focus on the essential features of Motoko and is the actively maintained standard library. + +To import the core library, the `import` keyword can be used at the start of your Motoko code file. After the `import` keyword, you need to provide a **local module name** and a **file path** that the import declaration can use to locate the imported module. For example, to import a local module named 'Debug,' you can use the following import statement: ```motoko no-repl no-repl -import Debug "mo:base/Debug"; +import Debug "mo:core/Debug"; ``` Then, to use this imported library, you can call the module with a line of code such as: @@ -83,7 +85,7 @@ Then, to use this imported library, you can call the module with a line of code Debug.print("Hello world!"); ``` -In this demonstration, you import Motoko code, indicated by the `mo:` prefix, then specify the `base/` path, indicating that you are using the base library, followed by the module's file name, `Debug.mo`. Note that the `.mo` extension is not included in the import statement. +In this demonstration, you import Motoko code, indicated by the `mo:` prefix, then specify the `core/` path, indicating that you are using the core library, followed by the module's file name, `Debug.mo`. Note that the `.mo` extension is not included in the import statement. Additionally, you can import Motoko code and other modules using their relative paths. For example, if you have a Motoko program named `types.mo` that you'd like to import into your Motoko file, you can use the following import declaration: @@ -91,7 +93,7 @@ Additionally, you can import Motoko code and other modules using their relative import Types "./types"; ``` -In this import declaration, you don't need to include the `mo:` prefix or the `base/` path, since you are importing a module from a local relative path rather than the base library. +In this import declaration, you don't need to include the `mo:` prefix or the `core/` path, since you are importing a module from a local relative path rather than the core library. ## Declarations and expressions @@ -241,7 +243,7 @@ Additionally, Motoko supports the following user-defined non-primitive value for Earlier, recall that you printed a value using the imported `Debug` library: ```motoko no-repl -import Debug "mo:base/Debug"; +import Debug "mo:core/Debug"; Debug.print("hello world"); ``` @@ -254,7 +256,7 @@ Next, let's look at transforming a Motoko value into a human-readable text strin The `debug_show` primitive value permits converting a large class of values into type `Text` values. For example, let's convert three values of type `Text`, `Nat`, and `Text` into human-readable text: ```motoko no-repl -import Debug "mo:base/Debug"; +import Debug "mo:core/Debug"; Debug.print(debug_show(("hello", 42, "world"))) ``` diff --git a/docs/tutorials/developer-liftoff/level-2/2.1-storage-persistence.mdx b/docs/tutorials/developer-liftoff/level-2/2.1-storage-persistence.mdx index 44957006cc..c80e69b2c6 100644 --- a/docs/tutorials/developer-liftoff/level-2/2.1-storage-persistence.mdx +++ b/docs/tutorials/developer-liftoff/level-2/2.1-storage-persistence.mdx @@ -57,7 +57,7 @@ This project can be opened in [ICP Ninja](https://icp.ninja/s/JwL2z) or you can ### Defining a stable variable ```motoko title="src/stable_storage_example_backend/main.mo" -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor Counter { @@ -108,7 +108,7 @@ dfx canister stop stable_storage_example_backend Then, let's alter the code of your canister. To keep things simple, you're going to change the counter increment value from '1' to '3'. Your altered code looks like this: ```motoko title="src/stable_storage_example_backend/main.mo" -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor Counter { diff --git a/docs/tutorials/developer-liftoff/level-2/2.2-advanced-canister-calls.mdx b/docs/tutorials/developer-liftoff/level-2/2.2-advanced-canister-calls.mdx index 99017311e7..793548f9cc 100644 --- a/docs/tutorials/developer-liftoff/level-2/2.2-advanced-canister-calls.mdx +++ b/docs/tutorials/developer-liftoff/level-2/2.2-advanced-canister-calls.mdx @@ -56,7 +56,7 @@ The amount of security your dapp needs depends on your dapp's use case and funct Queries are defined by the function modifier `query` in Motoko. Below is a simple query call used with a query the current time from the network: ```motoko -import Time "mo:base/Time"; +import Time "mo:core/Time"; actor QueryCall { @@ -80,8 +80,8 @@ In comparison to query calls, update calls have the opposite trade-off of perfor Update calls are not defined with a function modifier in Motoko like query calls are. Below is a simple update call that counts the number of characters within the inputted string, updates the value of the variable `size`, then returns a `true` or `false` value if that number of characters is divisible by 2. ```motoko -import Text "mo:base/Text"; -import Bool "mo:base/Bool"; +import Text "mo:core/Text"; +import Bool "mo:core/Bool"; actor countCharacters {     public func test(text : Text) : async Bool { diff --git a/docs/tutorials/developer-liftoff/level-2/2.5-unit-testing.mdx b/docs/tutorials/developer-liftoff/level-2/2.5-unit-testing.mdx index 597165c8a4..d101d22d68 100644 --- a/docs/tutorials/developer-liftoff/level-2/2.5-unit-testing.mdx +++ b/docs/tutorials/developer-liftoff/level-2/2.5-unit-testing.mdx @@ -27,7 +27,7 @@ There are three primary types of testing: ## Motoko unit testing -The Motoko base library contains a series of test files that can be used for unit testing, including tests for individual types, such as `Text`, `Array`, `Func`, etc. [Check out the full library of test files](https://github.com/dfinity/motoko-base/tree/master/test). +The Motoko core library (the recommended standard library) contains test files and examples that can be used for unit testing, including tests for types such as `Text`, `Array`, `Func`, etc. [Check out the motoko-core repository](https://github.com/caffeinelabs/motoko-core) for the library and its tests. Many of these test files import a library called Motoko Matchers, which contains several packages that can be imported and used to provide testing functionalities. diff --git a/docs/tutorials/developer-liftoff/level-2/2.6-motoko-lvl2.mdx b/docs/tutorials/developer-liftoff/level-2/2.6-motoko-lvl2.mdx index 61f60ee327..bfc96fd4c3 100644 --- a/docs/tutorials/developer-liftoff/level-2/2.6-motoko-lvl2.mdx +++ b/docs/tutorials/developer-liftoff/level-2/2.6-motoko-lvl2.mdx @@ -149,6 +149,10 @@ In this example, the following happens: Want to learn more about actor classes? Take a look at the documentation on [actor class management](/motoko/language-manual#actor-class-management) for more information. +:::info +The Buckets and Map samples above may use the older **base** library. For new projects, prefer the [core library](https://mops.one/core) and see the [base-to-core migration guide](https://docs.internetcomputer.org/motoko/base-core-migration) when moving to core. +::: + ## Using multiple actors Only one actor can be defined in a Motoko file, and a single actor is always compiled into a single canister. To create multiple actors, you'll need to create multiple Motoko files and build multiple canisters. diff --git a/docs/tutorials/developer-liftoff/level-3/3.1-package-managers.mdx b/docs/tutorials/developer-liftoff/level-3/3.1-package-managers.mdx index 05616e85d9..ab30f6c38a 100644 --- a/docs/tutorials/developer-liftoff/level-3/3.1-package-managers.mdx +++ b/docs/tutorials/developer-liftoff/level-3/3.1-package-managers.mdx @@ -28,7 +28,7 @@ ICP Ninja supports adding Mops packages to projects. For example, open the [Flyi # Motoko dependencies (https://mops.one/) [dependencies] -base = "0.14.1" +core = "0.0.0" # Check latest version at https://mops.one/core. Prefer core over base for new projects. ``` Simply edit this file with any Mops packages you'd like to use within your project. For example: @@ -105,23 +105,19 @@ You will also be prompted to choose if you'd like to use a GitHub workflow. This ### Adding packages to `mops.toml`. -To install a package with Mops, you need to specify the package in the `mops.toml` file within your project. To add a package to this file, you can use the command `maps add` and then the package name: +To install a package with Mops, you need to specify the package in the `mops.toml` file within your project. To add a package to this file, you can use the command `mops add` and then the package name. For the recommended standard library (core): ```bash -mops add base +mops add core ``` Or, you can add packages directly from GitHub by specifying the repository's URL: ```bash -mops add https://github.com/dfinity/motoko-base +mops add https://github.com/caffeinelabs/motoko-core ``` -You can also specify the branch, commit hash, or tag by adding `#`: - -```bash -mops add https://github.com/dfinity/motoko-base#moc-0.9.1 -``` +You can also specify the branch, commit hash, or tag by adding `#`. For existing projects using the older base library, see the [base-to-core migration guide](https://docs.internetcomputer.org/motoko/base-core-migration). If you have a locally stored package, you can put the source files inside your project's directory and then add them by specifying the path: @@ -148,15 +144,15 @@ If you develop a package that you'd like to publish so other developers can use Once a package has been installed, you can import it into your Motoko code file using the line `import PackageName "mo:";`, such as: ```motoko title="src/character_count/main.mo" -import Text "mo:base/Text"; -import Bool "mo:base/Bool"; +import Text "mo:core/Text"; +import Bool "mo:core/Bool"; actor characterCount { -    public func test(text: Text) : async Bool { -        let size = Text.size(text); -        return size % 2 == 0; -    }; + public func test(text: Text) : async Bool { + let size = Text.size(text); + return size % 2 == 0; + }; }; ``` diff --git a/docs/tutorials/developer-liftoff/level-3/3.3-certified-data.mdx b/docs/tutorials/developer-liftoff/level-3/3.3-certified-data.mdx index 15099ac10b..7e500c7b35 100644 --- a/docs/tutorials/developer-liftoff/level-3/3.3-certified-data.mdx +++ b/docs/tutorials/developer-liftoff/level-3/3.3-certified-data.mdx @@ -86,7 +86,7 @@ Developers can certify their canister's data and assets in two ways: ### Certified data Motoko module -The Motoko base library includes a module called `CertifiedData` that contains the following system API methods for certified data: +The Motoko core library includes a module called `CertifiedData` that contains the following system API methods for certified data: ```motoko no-repl let set : (data : Blob) -> () diff --git a/docs/tutorials/developer-liftoff/level-3/3.6-motoko-lvl3.mdx b/docs/tutorials/developer-liftoff/level-3/3.6-motoko-lvl3.mdx index 35b83b5c55..d293a7558a 100644 --- a/docs/tutorials/developer-liftoff/level-3/3.6-motoko-lvl3.mdx +++ b/docs/tutorials/developer-liftoff/level-3/3.6-motoko-lvl3.mdx @@ -25,7 +25,7 @@ In Motoko, recall that the `shared` keyword is used to declare a shared function To demonstrate this, consider the following function: ```motoko -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; shared(msg) func inc() : async () {   // ... msg.caller ... @@ -37,7 +37,7 @@ The shared function `inc()` specifies a `msg` parameter, a record, and the `msg. Additionally, the caller of an actor class can be returned using the same syntax on the actor class declaration, such as: ```motoko -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; shared(msg) actor class Counter(init : Nat) {   // ... msg.caller ... @@ -47,7 +47,7 @@ shared(msg) actor class Counter(init : Nat) { To further this example, imagine a scenario where you have a `Counter` actor that you want to restrict access to. You can modify the original code so that only the `principal` that installed the actor can modify it by recording the `principal` that installed the actor and binding it to an `owner` variable, then checking that the caller of each method is equal to the `owner` variable: ```motoko -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; shared(msg) actor class Counter(init : Nat) { @@ -126,122 +126,122 @@ dfx identity use owner Then, write the code that sets up the simple access control dapp. Open the `src/access_hello_backend/main.mo` file in your code editor and replace the existing content with the following code: ```motoko title="src/access_hello_backend/main.mo" -// Start by importing the base modules: +// Start by importing the core library modules (AssocList was removed in core; use Map instead): -import AssocList "mo:base/AssocList"; -import Error "mo:base/Error"; -import List "mo:base/List"; +import Error "mo:core/Error"; +import Iter "mo:core/Iter"; +import Map "mo:core/Map"; +import Principal "mo:core/Principal"; // Then, declare your shared actor class, which defines three role-based greetings to display using an if/else statement: shared({ caller = initializer }) actor class() { -    public shared({ caller }) func greet(name : Text) : async Text { -        if (has_permission(caller, #assign_role)) { -            return "Hello, " # name # ". You have a role with administrative privileges." -        } else if (has_permission(caller, #lowest)) { -            return "Welcome, " # name # ". You have an authorized account. Would you like to play a game?"; -        } else { -            return "Greetings, " # name # ". Nice to meet you!"; -        } -    }; + public shared({ caller }) func greet(name : Text) : async Text { + if (has_permission(caller, #assign_role)) { + return "Hello, " # name # ". You have a role with administrative privileges." + } else if (has_permission(caller, #lowest)) { + return "Welcome, " # name # ". You have an authorized account. Would you like to play a game?"; + } else { + return "Greetings, " # name # ". Nice to meet you!"; + } + }; // Next, define the custom types `Role` and `Permission`: -    public type Role = { -        #owner; -        #admin; -        #authorized; -    }; - -    public type Permission = { -        #assign_role; -        #lowest; -    }; - - // Define stable variables to store a list of principals and their associated role: -    private stable var roles: AssocList.AssocList = List.nil(); -    private stable var role_requests: AssocList.AssocList = List.nil(); - - // Create a function that checks if principal `a` equals principal `b`: -    func principal_eq(a: Principal, b: Principal): Bool { -        return a == b; -    }; - - // Define a function to get the principal's current role, first by checking if the principal is the initializer of the canister, then by checking the list that stores principals and their associated roles: - -    func get_role(pal: Principal) : ?Role { -        if (pal == initializer) { -            ?#owner; -        } else { -            AssocList.find(roles, pal, principal_eq); -        } -    }; + public type Role = { + #owner; + #admin; + #authorized; + }; + + public type Permission = { + #assign_role; + #lowest; + }; + + // Define stable mutable maps from Principal to Role (core Map replaces the old AssocList pattern): + private stable let roles = Map.empty(); + private stable let role_requests = Map.empty(); + + // Define a function to get the principal's current role, first by checking if the principal is the initializer of the canister, then by looking up in the roles map: + func get_role(pal: Principal) : ?Role { + if (pal == initializer) { + ?#owner; + } else { + Map.get(roles, Principal.compare, pal); + } + }; // Then, determine if a principal has a role with permissions: -    func has_permission(pal: Principal, perm : Permission) : Bool { -        let role = get_role(pal); -        switch (role, perm) { -            case (?#owner or ?#admin, _) true; -            case (?#authorized, #lowest) true; -            case (_, _) false; -        } -    }; + func has_permission(pal: Principal, perm : Permission) : Bool { + let role = get_role(pal); + switch (role, perm) { + case (?#owner or ?#admin, _) true; + case (?#authorized, #lowest) true; + case (_, _) false; + } + }; // Define a function that rejects any unauthorized principals: -    func require_permission(pal: Principal, perm: Permission) : async () { -        if ( has_permission(pal, perm) == false ) { -            throw Error.reject( "unauthorized" ); -        } -    }; + func require_permission(pal: Principal, perm: Permission) : async () { + if ( has_permission(pal, perm) == false ) { + throw Error.reject( "unauthorized" ); + } + }; // Define a function to assign a new role to a principal: -    public shared({ caller }) func assign_role( assignee: Principal, new_role: ?Role ) : async () { -        await require_permission( caller, #assign_role ); - -        switch new_role { -            case (?#owner) { -                throw Error.reject( "Cannot assign anyone to be the owner" ); -            }; -            case (_) {}; -        }; -        if (assignee == initializer) { -            throw Error.reject( "Cannot assign a role to the canister owner" ); -        }; -        roles := AssocList.replace(roles, assignee, principal_eq, new_role).0; -        role_requests := AssocList.replace(role_requests, assignee, principal_eq, null).0; -    }; - -    public shared({ caller }) func request_role( role: Role ) : async Principal { -        role_requests := AssocList.replace(role_requests, caller, principal_eq, ?role).0; -        return caller; -    }; + public shared({ caller }) func assign_role( assignee: Principal, new_role: ?Role ) : async () { + await require_permission( caller, #assign_role ); + + switch new_role { + case (?#owner) { + throw Error.reject( "Cannot assign anyone to be the owner" ); + }; + case (_) {}; + }; + if (assignee == initializer) { + throw Error.reject( "Cannot assign a role to the canister owner" ); + }; + switch new_role { + case (?r) { Map.add(roles, Principal.compare, assignee, r) }; + case null { ignore Map.take(roles, Principal.compare, assignee) }; + }; + ignore Map.take(role_requests, Principal.compare, assignee); + }; + + public shared({ caller }) func request_role( role: Role ) : async Principal { + Map.add(role_requests, Principal.compare, caller, role); + return caller; + }; // Return the principal of the message caller/user identity: -    public shared({ caller }) func callerPrincipal() : async Principal { -        return caller; -    }; + public shared({ caller }) func callerPrincipal() : async Principal { + return caller; + }; // Return the role of the message caller/user identity: -    public shared({ caller }) func my_role() : async ?Role { -        return get_role(caller); -    }; - -    public shared({ caller }) func my_role_request() : async ?Role { -        AssocList.find(role_requests, caller, principal_eq); -    }; - -    public shared({ caller }) func get_role_requests() : async List.List<(Principal,Role)> { -        await require_permission( caller, #assign_role ); -        return role_requests; -    }; - -    public shared({ caller }) func get_roles() : async List.List<(Principal,Role)> { -        await require_permission( caller, #assign_role ); -        return roles; -    }; + public shared({ caller }) func my_role() : async ?Role { + return get_role(caller); + }; + + public shared({ caller }) func my_role_request() : async ?Role { + Map.get(role_requests, Principal.compare, caller); + }; + + public shared({ caller }) func get_role_requests() : async [(Principal, Role)] { + await require_permission( caller, #assign_role ); + Iter.toArray(Map.entries(role_requests)); + }; + + public shared({ caller }) func get_roles() : async [(Principal, Role)] { + await require_permission( caller, #assign_role ); + Iter.toArray(Map.entries(roles)); + }; }; ``` +This example uses the **core** library's `Map` to store principal-to-role mappings (the older **base** library used `AssocList`; see the [base-to-core migration guide](https://docs.internetcomputer.org/motoko/base-core-migration) for details). + The key components of this dapp are: - A `greet` function that uses a message caller to determine the permissions that should be applied to a principal, then displays a greeting depending on the permissions associated with the caller. @@ -382,7 +382,7 @@ In Motoko, **pattern matching** is a feature for testing and deconstructing stru The input in this callee is an anonymous object, which is then deconstructed into the object's three `Text` fields that are bound to the identifiers `first`, `mid`, and `last`. These values can be freely used in the block that forms the body of the function. ```motoko -import Text "mo:base/Text"; +import Text "mo:core/Text"; actor {     func fullName({ first : Text; mid : Text; last : Text }) : Text { diff --git a/docs/tutorials/developer-liftoff/level-3/index.mdx b/docs/tutorials/developer-liftoff/level-3/index.mdx index 572482e50a..8d7a0f0953 100644 --- a/docs/tutorials/developer-liftoff/level-3/index.mdx +++ b/docs/tutorials/developer-liftoff/level-3/index.mdx @@ -8,7 +8,7 @@ import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow"; -- [3.1 Motoko package managers](/tutorials/developer-liftoff/level-3/3.1-package-managers): To use packages developed outside of the Motoko base library, a Motoko package manager can be used. +- [3.1 Motoko package managers](/tutorials/developer-liftoff/level-3/3.1-package-managers): To use packages developed outside of the Motoko core library (the standard library), a Motoko package manager can be used. - [3.2 Using HTTPS outcalls](/tutorials/developer-liftoff/level-3/3.2-https-outcalls): HTTPS outcalls are a feature of canisters on ICP that allow smart contracts to directly make calls to HTTP servers that are external to ICP. diff --git a/docs/tutorials/developer-liftoff/level-4/4.6-motoko-lvl4.mdx b/docs/tutorials/developer-liftoff/level-4/4.6-motoko-lvl4.mdx index c4959e7341..f0f6cd9f86 100644 --- a/docs/tutorials/developer-liftoff/level-4/4.6-motoko-lvl4.mdx +++ b/docs/tutorials/developer-liftoff/level-4/4.6-motoko-lvl4.mdx @@ -37,8 +37,8 @@ To understand mutable state, let's take a look at an example where an actor's pr Immutable variables use the `let` syntax when being declared: ```motoko -import Text "mo:base/Text"; -import Nat "mo:base/Nat"; +import Text "mo:core/Text"; +import Nat "mo:core/Nat"; actor {   let textImmutable  : Text = "Developer Liftoff"; @@ -49,8 +49,8 @@ actor { Mutable variables use the `var` syntax when being declared: ```motoko no-repl -import Text "mo:base/Text"; -import Nat "mo:base/Nat"; +import Text "mo:core/Text"; +import Nat "mo:core/Nat"; actor {   var pairMutable : (Text, Nat) = (textImmutable, numImmutable); @@ -75,7 +75,7 @@ An actor processes calls by performing updates on its internal mutable variables When data is read from the actor's state, each mutable and immutable variable will look alike; however, they will behave differently. For example, consider the following variable declarations: ```motoko -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor {   let x : Nat = 0 @@ -86,7 +86,7 @@ actor { and: ```motoko -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor {   var x : Nat = 0 @@ -102,7 +102,7 @@ In the mutable variable definition using `var`, each occurrence of the variable `let` bound variables are more fundamental to a program. To demonstrate why, consider the following `var` bound variable that uses a single element, a mutable array, that is bound to itself using a `let` bound variable: ```motoko -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor {   var x : Nat       = 0 ; @@ -115,9 +115,9 @@ In this example, instead of declaring `x` as a mutable variable that initially h It is important to note that the read/write syntax for mutable arrays is not as readable as `var` bound variables; therefore, reading and writing to variable `x` will be easier than variable `y`. :::info -The Motoko standard library provides an Array library that can be used for both mutable and immutable arrays. This library can be imported with the statement: +The Motoko core library provides an **Array** module for immutable arrays and a **VarArray** module for mutable arrays. Import them with: -`import Array "mo:base/Array";` +`import Array "mo:core/Array";` and `import VarArray "mo:core/VarArray";` ::: ### Immutable arrays @@ -127,7 +127,7 @@ Before going deeper into mutable arrays, first let's cover immutable arrays, whi For example, consider the following immutable array: ```motoko no-repl -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor {   let a : [Nat] = [1, 2, 3] ; @@ -137,7 +137,7 @@ actor { Then, to read from this array you can use the traditional bracket syntax of `[` and `]` to read the desired index: ```motoko -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor {   let x : Nat = a[2] + a[0] ; @@ -153,7 +153,7 @@ Mutable arrays cannot be used in place of immutable ones, since the Motoko defin To declare a mutable array, the `[var _]` syntax is used. Note that this uses the `var` keyword for both the expression and types: ```motoko -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor {   let a : [var Nat] = [var 1, 2, 3] ; @@ -162,15 +162,15 @@ actor { This array holds three numbers with type `Nat`. These three values can be updated, but the number of entries cannot. -To declare a mutable array with a dynamic size, such that the number of entries within the array can be updated, the following `Array_init` syntax can be used: +To declare a mutable array with a dynamic size, such that the number of entries within the array can be updated, use the **VarArray** module from the core library (in core, `Array.init` was removed; use `VarArray` for mutable arrays): ```motoko no-repl -import Nat "mo:base/Nat"; -import Array "mo:base/Array"; +import Nat "mo:core/Nat"; +import VarArray "mo:core/VarArray"; actor { -  ar size : Nat; -  let x : [var Nat] = Array.init(size, 5); + let size : Nat = 10; + let x : [var Nat] = VarArray.tabulate(size, func(_) = 5); } ``` @@ -180,7 +180,7 @@ Using this syntax, this mutable array will have `size` number of entries, each w x[2] := 42; ``` -Using this syntax, each entry can be updated via an assignment to that individual entry. In this example, the index entry of `2` gets updated from equaling `52` to equal `42`. +Using this syntax, each entry can be updated via an assignment to that individual entry. In this example, the index entry of `2` gets updated from `5` to `42`. :::caution @@ -214,7 +214,7 @@ First, let's clearly define the difference between object and actor classes: For example, let's look at the following object declaration of the object `counter`: ```motoko -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor {   object counter { @@ -257,7 +257,7 @@ In Motoko, object subtyping refers to objects that may have various types. Objec To demonstrate an example of subtyping, let's use a simple counter with a more general type that uses fewer public functions: ```motoko -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor {   object bumpCounter { @@ -307,7 +307,7 @@ If a function expects to receive an object using the first type `({ bump: () → An object class is a package of entities that share a common name. For example, let's define a class example for counters that start at zero: ```motoko no-repl -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor {  class Counter() { @@ -342,7 +342,7 @@ actor{ In comparison, the same results can be achieved by using a constructor function to return an object: ```motoko -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor {   func Counter() : { inc : () -> Nat } = @@ -358,7 +358,7 @@ actor { In your object class, you initialized counter functions to start with a value of zero. If you want to initialize the counter with a value other than zero, you can pass a data argument into the constructor function: ```motoko -import Nat "mo:base/Nat"; +import Nat "mo:core/Nat"; actor { class Counter(init : Nat) { @@ -405,8 +405,8 @@ system func inspect({}) : Bool { false } In comparison, the following `inspect` function example declines all messages where the principal value is anonymous: ```motoko -import Principal "mo:base/Principal"; -import Bool "mo:base/Bool"; +import Principal "mo:core/Principal"; +import Bool "mo:core/Bool"; actor {   system func inspect({ caller : Principal }) : Bool { @@ -418,7 +418,7 @@ actor { Messages can also be declined based on their raw size in bytes (of their arguments prior to any decoding from the Candid binary to the Motoko value), such as: ```motoko -import Bool "mo:base/Bool"; +import Bool "mo:core/Bool"; actor {   system func inspect({ arg : Blob }) : Bool { @@ -447,7 +447,7 @@ Motoko error handling best practices recommend using `Option` and `Result` error If a function wants to return the value of type `A` or signal an error, it can return a value of option type `?A`; the `null` value can designate the error. For example, consider the following `markDone` function that returns an async value of `?Seconds`. ```motoko no-repl -import Time "mo:base/Time"; +import Time "mo:core/Time"; actor {   public shared func markDoneOption(id : TodoId) : async ?Seconds { @@ -467,8 +467,8 @@ Then, you can use the following error call site: ```motoko no-repl -import Int "mo:base/Int"; -import Text "mo:base/Text"; +import Int "mo:core/Int"; +import Text "mo:core/Text"; actor {   public shared func doneTodo2(id : Todo.TodoId) : async Text { @@ -497,8 +497,8 @@ type Result = { #ok : Ok; #err : Err } Since `Result` includes the `Err` type parameters, the `Result` type allows you to select the type used to describe errors. Consider the following example that uses `Result` variants to signal errors: ```motoko no-repl -import Result "mo:base/Result"; -import Time "mo:base/Time"; +import Result "mo:core/Result"; +import Time "mo:core/Time"; actor {   public shared func markDoneResult(id : TodoId) : async Result.Result { @@ -522,9 +522,9 @@ actor { Then, you can use the following error call site: ```motoko no-repl -import Time "mo:base/Time"; -import Text "mo:base/Text"; -import Int "mo:base/Int"; +import Time "mo:core/Time"; +import Text "mo:core/Text"; +import Int "mo:core/Int"; actor {   public shared func doneTodo3(id : Todo.TodoId) : async Text { @@ -555,7 +555,7 @@ Asynchronous errors are a restricted form of exception error handling. Since Mot Asynchronous error handling generally should only be used for catching and signaling unexpected, irrecoverable failures. If a failure can be handled by the caller, it should return a `Result` instead. The following example demonstrates a form of asynchronous error handling: ```motoko no-repl -import Time "mo:base/Time"; +import Time "mo:core/Time"; actor {   public shared func markDoneException(id : TodoId) : async Seconds { @@ -579,7 +579,7 @@ actor { Then, you can use the following error call site: ```motoko no-repl -import Text "mo:base/Text"; +import Text "mo:core/Text"; actor {   public shared func doneTodo4(id : Todo.TodoId) : async Text { diff --git a/plugins/utils/redirects.js b/plugins/utils/redirects.js index 4dbf11f31f..1e833151ef 100644 --- a/plugins/utils/redirects.js +++ b/plugins/utils/redirects.js @@ -294,6 +294,13 @@ const redirects = ` /current/motoko/main/base/trieset /motoko/base/TrieSet /motoko/main/base/TrieSet /motoko/base/TrieSet /current/motoko/main/base/TrieSet /motoko/base/TrieSet + # Motoko base -> core (recommended standard library) + /motoko/base/Debug /motoko/core/Debug + /motoko/base/Timer /motoko/core/Timer + /motoko/base/Random /motoko/core/Random + /motoko/base/ExperimentalCycles /motoko/core/Cycles + /motoko/base/ExperimentalInternetComputer /motoko/core/InternetComputer + /motoko/main/canister-maintenance/compatibility /motoko/home /current/motoko/main/canister-maintenance/compatibility /motoko/home /motoko/main/canister-maintenance/cycles /building-apps/getting-started/tokens-and-cycles