Map implemented as a linked-list of key-value pairs ("Associations").
NOTE: This map implementation is mainly used as underlying buckets for other map structures. Thus, other map implementations are easier to use in most cases.
Module for working with Blobs: immutable sequence of bytes.
Blobs represent sequences of bytes. They are immutable, iterable, but not indexable and can be empty.
Byte sequences are also often represented as [Nat8]
, i.e. an array of bytes, but this representation is currently much less compact than Blob
, taking 4 physical bytes to represent each logical byte in the sequence.
If you would like to manipulate Blobs, it is recommended that you convert
Blobs to [var Nat8]
or Buffer<Nat8>
, do the manipulation, then convert back.
Import from the base library to use this module.
motoko name=import
import Blob "mo:base/Blob";
Some built in features not listed in this module:
Blob
literal from a Text
literal, provided the context expects an expression of type Blob
.b.size() : Nat
returns the number of bytes in the blob b
;b.vals() : Iter.Iter<Nat8>
returns an iterator to enumerate the bytes of the blob b
.For example:
motoko include=import
import Debug "mo:base/Debug";
import Nat8 "mo:base/Nat8";
let blob = "\00\00\00\ff" : Blob; // blob literals, where each byte is delimited by a back-slash and represented in hex
let blob2 = "charsγγγ" : Blob; // you can also use characters in the literals
let numBytes = blob.size(); // => 4 (returns the number of bytes in the Blob)
for (byte : Nat8 in blob.vals()) { // iterator over the Blob
Debug.print(Nat8.toText(byte))
}
Boolean type and operations.
While boolean operators _ and _
and _ or _
are short-circuiting,
avoiding computation of the right argument when possible, the functions
logand(_, _)
and logor(_, _)
are strict and will always evaluate both
of their arguments.
Class Buffer<X>
provides a mutable list of elements of type X
.
The class wraps and resizes an underyling array that holds the elements,
and thus is comparable to ArrayLists or Vectors in other languages.
When required, the current state of a buffer object can be converted to a fixed-size array of its elements. This is recommended for example when storing a buffer to a stable variable.
Throughout this documentation, two terms come up that can be confused: size
and capacity
. size
is the length of the list that the buffer represents.
capacity
is the length of the underyling array that backs this list.
capacity
>= size
is an invariant for this class.
Like arrays, elements in the buffer are ordered by indices from 0 to size
-1.
WARNING: Certain operations are amortized O(1) time, such as add
, but run
in worst case O(n) time. These worst case runtimes may exceed the cycles limit
per message if the size of the buffer is large enough. Grow these structures
with discretion. All amortized operations below also list the worst case runtime.
Constructor:
The argument initCapacity
determines the initial capacity of the array.
The underlying array grows by a factor of 1.5 when its current capacity is
exceeded. Further, when the size of the buffer shrinks to be less than 1/4th
of the capacity, the underyling array is shrunk by a factor of 2.
Example:
motoko name=initialize
import Buffer "mo:base/Buffer";
let buffer = Buffer.Buffer<Nat>(3); // Creates a new Buffer
Runtime: O(initCapacity)
Space: O(initCapacity)
Certified data.
The Internet Computer allows canister smart contracts to store a small amount of data during update method processing so that during query call processing, the canister can obtain a certificate about that data.
This module provides a low-level interface to this API, aimed at advanced users and library implementors. See the Internet Computer Functional Specification and corresponding documentation for how to use this to make query calls to your canister tamperproof.
Characters
Utility functions for debugging.
Import from the base library to use this module.
motoko name=import
import Debug "mo:base/Debug";
Double-ended queue (deque) of a generic element type T
.
The interface to deques is purely functional, not imperative, and deques are immutable values. In particular, deque operations such as push and pop do not update their input deque but, instead, return the value of the modified deque, alongside any other data. The input deque is left unchanged.
Examples of use-cases:
Queue (FIFO) by using pushBack()
and popFront()
.
Stack (LIFO) by using pushFront()
and popFront()
.
A deque is internally implemented as two lists, a head access list and a (reversed) tail access list, that are dynamically size-balanced by splitting.
Construction: Create a new deque with the empty<T>()
function.
Note on the costs of push and pop functions:
O(1) amortized costs,
O(n)` worst case cost per single call.O(1) amortized costs,
O(n)` worst case cost per single call.n
denotes the number of elements stored in the deque.
Error values and inspection.
The Error
type is the argument to throw
, parameter of catch
.
The Error
type is opaque.
Managing cycles within actors on the Internet Computer (IC).
The usage of the Internet Computer is measured, and paid for, in cycles. This library provides imperative operations for observing cycles, transferring cycles, and observing refunds of cycles.
WARNING: This low-level API is experimental and likely to change or even disappear. Dedicated syntactic support for manipulating cycles may be added to the language in future, obsoleting this library.
NOTE: Since cycles measure computational resources, the value of balance()
can change from one call to the next.
Example for use on IC:
motoko no-repl
import Cycles "mo:base/ExperimentalCycles";
import Debug "mo:base/Debug";
actor {
public func main() : async() {
Debug.print("Main balance: " # debug_show(Cycles.balance()));
Cycles.add<system>(15_000_000);
await operation(); // accepts 10_000_000 cycles
Debug.print("Main refunded: " # debug_show(Cycles.refunded())); // 5_000_000
Debug.print("Main balance: " # debug_show(Cycles.balance())); // decreased by around 10_000_000
};
func operation() : async() {
Debug.print("Operation balance: " # debug_show(Cycles.balance()));
Debug.print("Operation available: " # debug_show(Cycles.available()));
let obtained = Cycles.accept<system>(10_000_000);
Debug.print("Operation obtained: " # debug_show(obtained)); // => 10_000_000
Debug.print("Operation balance: " # debug_show(Cycles.balance())); // increased by 10_000_000
Debug.print("Operation available: " # debug_show(Cycles.available())); // decreased by 10_000_000
}
}
Low-level interface to the Internet Computer.
WARNING: This low-level API is experimental and likely to change or even disappear.
Byte-level access to (virtual) stable memory.
WARNING: As its name suggests, this library is experimental, subject to change and may be replaced by safer alternatives in later versions of Motoko. Use at your own risk and discretion.
DEPRECATION: Use of ExperimentalStableMemory
library may be deprecated in future.
Going forward, users should consider using library Region.mo
to allocate isolated regions of memory instead.
Using dedicated regions for different user applications ensures that writing
to one region will not affect the state of another, unrelated region.
This is a lightweight abstraction over IC stable memory and supports persisting raw binary data across Motoko upgrades. Use of this module is fully compatible with Motoko's use of stable variables, whose persistence mechanism also uses (real) IC stable memory internally, but does not interfere with this API.
Memory is allocated, using grow(pages)
, sequentially and on demand, in units of 64KiB pages, starting with 0 allocated pages.
New pages are zero initialized.
Growth is capped by a soft limit on page count controlled by compile-time flag
--max-stable-pages <n>
(the default is 65536, or 4GiB).
Each load
operation loads from byte address offset
in little-endian
format using the natural bit-width of the type in question.
The operation traps if attempting to read beyond the current stable memory size.
Each store
operation stores to byte address offset
in little-endian format using the natural bit-width of the type in question.
The operation traps if attempting to write beyond the current stable memory size.
Text values can be handled by using Text.decodeUtf8
and Text.encodeUtf8
, in conjunction with loadBlob
and storeBlob
.
The current page allocation and page contents is preserved across upgrades.
NB: The IC's actual stable memory size (ic0.stable_size
) may exceed the
page size reported by Motoko function size()
.
This (and the cap on growth) are to accommodate Motoko's stable variables.
Applications that plan to use Motoko stable variables sparingly or not at all can
increase --max-stable-pages
as desired, approaching the IC maximum (initially 8GiB, then 32Gib, currently 64Gib).
All applications should reserve at least one page for stable variable data, even when no stable variables are used.
Usage:
motoko no-repl
import StableMemory "mo:base/ExperimentalStableMemory";
Double precision (64-bit) floating-point numbers in IEEE 754 representation.
This module contains common floating-point constants and utility functions.
Notation for special values in the documentation below:
+inf
: Positive infinity
-inf
: Negative infinity
NaN
: "not a number" (can have different sign bit values, but NaN != NaN
regardless of the sign).
Note: Floating point numbers have limited precision and operations may inherently result in numerical errors.
Examples of numerical errors:
0.1 + 0.1 + 0.1 == 0.3 // => false
1e16 + 1.0 != 1e16 // => false
(and many more cases)
Advice:
==
or !=
are discouraged. Instead, it is better to compare
floating-point numbers with a numerical tolerance, called epsilon.Example:
import Float "mo:base/Float";
let x = 0.1 + 0.1 + 0.1;
let y = 0.3;
let epsilon = 1e-6; // This depends on the application case (needs a numerical error analysis).
Float.equalWithin(x, y, epsilon) // => true
NaN sign:
abs
, neg
, and copySign
. Other operations can have an arbitrary
sign bit for NaN results.Functions on functions, creating functions from simpler inputs.
(Most commonly used when programming in functional style using higher-order functions.)
Hash values
Class HashMap<K, V>
provides a hashmap from keys of type K
to values of type V
.
The class is parameterized by the key's equality and hash functions,
and an initial capacity. However, the underlying allocation happens only when
the first key-value entry is inserted.
Internally, the map is represented as an array of AssocList
(buckets).
The growth policy of the underyling array is very simple, for now: double
the current capacity when the expected bucket list size grows beyond a
certain constant.
WARNING: Certain operations are amortized O(1) time, such as put
, but run
in worst case O(size) time. These worst case runtimes may exceed the cycles limit
per message if the size of the map is large enough. Further, this runtime analysis
assumes that the hash functions uniformly maps keys over the hash space. Grow these structures
with discretion, and with good hash functions. All amortized operations
below also list the worst case runtime.
For maps without amortization, see TrieMap
.
Note on the constructor:
The argument initCapacity
determines the initial number of buckets in the
underyling array. Also, the runtime and space anlyses in this documentation
assumes that the equality and hash functions for keys used to construct the
map run in O(1) time and space.
Example:
motoko name=initialize
import HashMap "mo:base/HashMap";
import Text "mo:base/Text";
let map = HashMap.HashMap<Text, Nat>(5, Text.equal, Text.hash);
Runtime: O(1)
Space: O(1)
Class Heap<X>
provides a priority queue of elements of type X
.
The class wraps a purely-functional implementation based on a leftist heap.
Note on the constructor:
The constructor takes in a comparison function compare
that defines the
ordering between elements of type X
. Most primitive types have a default
version of this comparison function defined in their modules (e.g. Nat.compare
).
The runtime analysis in this documentation assumes that the compare
function
runs in O(1)
time and space.
Example:
motoko name=initialize
import Heap "mo:base/Heap";
import Text "mo:base/Text";
let heap = Heap.Heap<Text>(Text.compare);
Runtime: O(1)
Space: O(1)
Signed integer numbers with infinite precision (also called big integers).
Most operations on integer numbers (e.g. addition) are available as built-in operators (e.g. -1 + 1
).
This module provides equivalent functions and Text
conversion.
Import from the base library to use this module.
motoko name=import
import Int "mo:base/Int";
Provides utility functions on 16-bit signed integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Int16 "mo:base/Int16";
Provides utility functions on 32-bit signed integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Int32 "mo:base/Int32";
Provides utility functions on 64-bit signed integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Int64 "mo:base/Int64";
Provides utility functions on 8-bit signed integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Int8 "mo:base/Int8";
Iterators
The Iterator type
Purely-functional, singly-linked lists.
A list of type List<T>
is either null
or an optional pair of a value of type T
and a tail, itself of type List<T>
.
To use this library, import it using:
motoko name=initialize
import List "mo:base/List";
Natural numbers with infinite precision.
Most operations on natural numbers (e.g. addition) are available as built-in operators (e.g. 1 + 1
).
This module provides equivalent functions and Text
conversion.
Import from the base library to use this module.
motoko name=import
import Nat "mo:base/Nat";
Provides utility functions on 16-bit unsigned integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Nat16 "mo:base/Nat16";
Provides utility functions on 32-bit unsigned integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Nat32 "mo:base/Nat32";
Provides utility functions on 64-bit unsigned integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Nat64 "mo:base/Nat64";
Provides utility functions on 8-bit unsigned integers.
Note that most operations are available as built-in operators (e.g. 1 + 1
).
Import from the base library to use this module.
motoko name=import
import Nat8 "mo:base/Nat8";
The absent value
The None
type represents a type with no value.
It is often used to type code that fails to return control (e.g. an infinite loop)
or to designate impossible values (e.g. the type ?None
only contains null
).
Typesafe nulls
Optional values can be seen as a typesafe null
. A value of type ?Int
can
be constructed with either null
or ?42
. The simplest way to get at the
contents of an optional is to use pattern matching:
let optionalInt1 : ?Int = ?42;
let optionalInt2 : ?Int = null;
let int1orZero : Int = switch optionalInt1 {
case null 0;
case (?int) int;
};
assert int1orZero == 42;
let int2orZero : Int = switch optionalInt2 {
case null 0;
case (?int) int;
};
assert int2orZero == 0;
The functions in this module capture some common operations when working with optionals that can be more succinct than using pattern matching.
Order
Stable key-value map implemented as a red-black tree with nodes storing key-value pairs.
A red-black tree is a balanced binary search tree ordered by the keys.
The tree data structure internally colors each of its nodes either red or black, and uses this information to balance the tree during the modifying operations.
Performance:
O(log(n))
worst case cost per insertion, removal, and retrieval operation.O(n)
for storing the entire tree.
n
denotes the number of key-value entries (i.e. nodes) stored in the tree.Note:
O(log(n))
temporary objects that become garbage.Credits:
The core of this implementation is derived from:
Stable ordered set implemented as a red-black tree.
A red-black tree is a balanced binary search tree ordered by the elements.
The tree data structure internally colors each of its nodes either red or black, and uses this information to balance the tree during the modifying operations.
Performance:
O(log(n))
worst case cost per insertion, removal, and retrieval operation.O(n)
for storing the entire tree.
n
denotes the number of elements (i.e. nodes) stored in the tree.Credits:
The core of this implementation is derived from:
General utilities
This prelude file proposes standard library features that may belong in the language (compiler-internal) prelude sometime, after some further experience and discussion. Until then, they live here.
Module for interacting with Principals (users and canisters).
Principals are used to identify entities that can interact with the Internet Computer. These entities are either users or canisters.
Example textual representation of Principals:
un4fu-tqaaa-aaaab-qadjq-cai
In Motoko, there is a primitive Principal type called Principal
. As an example
of where you might see Principals, you can access the Principal of the
caller of your shared function.
motoko no-repl
shared(msg) func foo() {
let caller : Principal = msg.caller;
};
Then, you can use this module to work with the Principal
.
Import from the base library to use this module.
motoko name=import
import Principal "mo:base/Principal";
Key-value map implemented as a red-black tree (RBTree) with nodes storing key-value pairs.
A red-black tree is a balanced binary search tree ordered by the keys.
The tree data structure internally colors each of its nodes either red or black, and uses this information to balance the tree during the modifying operations.
Creation:
Instantiate class RBTree<K, V>
that provides a map from keys of type K
to values of type V
.
Example:
import RBTree "mo:base/RBTree";
import Nat "mo:base/Nat";
import Debug "mo:base/Debug";
let tree = RBTree.RBTree<Nat, Text>(Nat.compare); // Create a new red-black tree mapping Nat to Text
tree.put(1, "one");
tree.put(2, "two");
tree.put(3, "tree");
for (entry in tree.entries()) {
Debug.print("Entry key=" # debug_show(entry.0) # " value=\"" # entry.1 #"\"");
}
Performance:
O(log(n))
worst case cost per insertion, removal, and retrieval operation.O(n)
for storing the entire tree.O(log(n)) for storing the entire tree.
n` denotes the number of key-value entries (i.e. nodes) stored in the tree.Note:
O(log(n))
garbage objects.Credits:
The core of this implementation is derived from:
A module for obtaining randomness on the Internet Computer (IC).
This module provides the fundamentals for user abstractions to build on.
Dealing with randomness on a deterministic computing platform, such as the IC, is intricate. Some basic rules need to be followed by the user of this module to obtain (and maintain) the benefits of crypto- graphic randomness:
Blob
s)Blob
) - or surplus entropy
not utilised yet - cannot be used for a new round of bets without
losing the cryptographic guarantees.Concretely, the below class Finite
, as well as the
*From
methods risk the carrying-over of state from previous rounds.
These are provided for performance (and convenience) reasons, and need
special care when used. Similar caveats apply for user-defined (pseudo)
random number generators.
Usage:
motoko no-repl
import Random "mo:base/Random";
Byte-level access to isolated, (virtual) stable memory regions.
This is a moderately lightweight abstraction over IC stable memory and supports persisting
regions of binary data across Motoko upgrades.
Use of this module is fully compatible with Motoko's use of
stable variables, whose persistence mechanism also uses (real) IC stable memory internally, but does not interfere with this API.
It is also fully compatible with existing uses of the ExperimentalStableMemory
library, which has a similar interface, but,
only supported a single memory region, without isolation between different applications.
The Region
type is stable and can be used in stable data structures.
A new, empty Region
is allocated using function new()
.
Regions are stateful objects and can be distinguished by the numeric identifier returned by function id(region)
.
Every region owns an initially empty, but growable sequence of virtual IC stable memory pages.
The current size, in pages, of a region is returned by function size(region)
.
The size of a region determines the range, [ 0, ..., size(region)*2^16 ), of valid byte-offsets into the region; these offsets are used as the source and destination of load
/store
operations on the region.
Memory is allocated to a region, using function grow(region, pages)
, sequentially and on demand, in units of 64KiB logical pages, starting with 0 allocated pages.
A call to grow
may succeed, returning the previous size of the region, or fail, returning a sentinel value. New pages are zero initialized.
A size of a region can only grow and never shrink. In addition, the stable memory pages allocated to a region will not be reclaimed by garbage collection, even if the region object itself becomes unreachable.
Growth is capped by a soft limit on physical page count controlled by compile-time flag
--max-stable-pages <n>
(the default is 65536, or 4GiB).
Each load
operation loads from region relative byte address offset
in little-endian
format using the natural bit-width of the type in question.
The operation traps if attempting to read beyond the current region size.
Each store
operation stores to region relative byte address offset
in little-endian format using the natural bit-width of the type in question.
The operation traps if attempting to write beyond the current region size.
Text values can be handled by using Text.decodeUtf8
and Text.encodeUtf8
, in conjunction with loadBlob
and storeBlob
.
The current region allocation and region contents are preserved across upgrades.
NB: The IC's actual stable memory size (ic0.stable_size
) may exceed the
total page size reported by summing all regions sizes.
This (and the cap on growth) are to accommodate Motoko's stable variables and bookkeeping for regions.
Applications that plan to use Motoko stable variables sparingly or not at all can
increase --max-stable-pages
as desired, approaching the IC maximum (initially 8GiB, then 32Gib, currently 64Gib).
All applications should reserve at least one page for stable variable data, even when no stable variables are used.
Usage:
motoko no-repl
import Region "mo:base/Region";
Error handling with the Result type.
Class Stack<X>
provides a Minimal LIFO stack of elements of type X
.
See library Deque
for mixed LIFO/FIFO behavior.
Example:
motoko name=initialize
import Stack "mo:base/Stack";
let stack = Stack.Stack<Nat>(); // create a stack
Runtime: O(1)
Space: O(1)
Utility functions for Text
values.
A Text
value represents human-readable text as a sequence of characters of type Char
.
let text = "Hello!";
let size = text.size(); // 6
let iter = text.chars(); // iterator ('H', 'e', 'l', 'l', 'o', '!')
let concat = text # " π"; // "Hello! π"
The "mo:base/Text"
module defines additional operations on Text
values.
Import the module from the base library:
motoko name=import
import Text "mo:base/Text";
Note: Text
values are represented as ropes of UTF-8 character sequences with O(1) concatenation.
System time
Timers for one-off or periodic tasks.
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.
Functional key-value hash maps.
This module provides an applicative (functional) hash map, called a trie. Notably, each operation produces a new trie rather than destructively updating an existing trie.
Those looking for a more familiar (imperative,
object-oriented) hash map should consider TrieMap
or HashMap
instead.
The basic Trie
operations consist of:
put
- put a key-value into the trie, producing a new version.get
- get a key's value from the trie, or null
if none.remove
- remove a key's value from the trieiter
- visit every key-value in the trie.The put
, get
and remove
operations work over Key
records,
which group the hash of the key with its non-hash key value.
LIMITATIONS: This data structure allows at most MAX_LEAF_SIZE=8 hash collisions:
attempts to insert more than MAX_LEAF_SIZE keys (whether directly via put
or indirectly via other operations) with the same hash value will trap.
CREDITS: Based on Section 6 of "Incremental computation via function caching", Pugh & Teitelbaum.
Example:
import Trie "mo:base/Trie";
import Text "mo:base/Text";
// we do this to have shorter type names and thus
// better readibility
type Trie<K, V> = Trie.Trie<K, V>;
type Key<K> = Trie.Key<K>;
// we have to provide `put`, `get` and `remove` with
// a record of type `Key<K> = { hash : Hash.Hash; key : K }`;
// thus we define the following function that takes a value of type `K`
// (in this case `Text`) and returns a `Key<K>` record.
func key(t: Text) : Key<Text> { { hash = Text.hash t; key = t } };
// we start off by creating an empty `Trie`
let t0 : Trie<Text, Nat> = Trie.empty();
// `put` requires 4 arguments:
// - the trie we want to insert the value into,
// - the key of the value we want to insert (note that we use the `key` function defined above),
// - a function that checks for equality of keys, and
// - the value we want to insert.
//
// When inserting a value, `put` returns a tuple of type `(Trie<K, V>, ?V)`.
// to get the new trie that contains the value, we use the `0` projection
// and assign it to `t1` and `t2` respectively.
let t1 : Trie<Text, Nat> = Trie.put(t0, key "hello", Text.equal, 42).0;
let t2 : Trie<Text, Nat> = Trie.put(t1, key "world", Text.equal, 24).0;
// If for a given key there already was a value in the trie, `put` returns
// that previous value as the second element of the tuple.
// in our case we have already inserted the value 42 for the key "hello", so
// `put` returns 42 as the second element of the tuple.
let (t3, n) : (Trie<Text, Nat>, ?Nat) = Trie.put(
t2,
key "hello",
Text.equal,
0,
);
assert (n == ?42);
// `get` requires 3 arguments:
// - the trie we want to get the value from
// - the key of the value we want to get (note that we use the `key` function defined above)
// - a function that checks for equality of keys
//
// If the given key is nonexistent in the trie, `get` returns `null`.
var value = Trie.get(t3, key "hello", Text.equal); // Returns `?42`
assert(value == ?0);
value := Trie.get(t3, key "universe", Text.equal); // Returns `null`
assert(value == null);
// `remove` requires 3 arguments:
// - the trie we want to remove the value from,
// - the key of the value we want to remove (note that we use the `key` function defined above), and
// - a function that checks for equality of keys.
//
// In the case of keys of type `Text`, we can use `Text.equal`
// to check for equality of keys. Function `remove` returns a tuple of type `(Trie<K, V>, ?V)`.
// where the second element of the tuple is the value that was removed, or `null` if
// there was no value for the given key.
let removedValue : ?Nat = Trie.remove(
t3,
key "hello",
Text.equal,
).1;
assert (removedValue == ?0);
// To iterate over the Trie, we use the `iter` function that takes a trie
// of type `Trie<K,V>` and returns an iterator of type `Iter<(K,V)>`:
var sum : Nat = 0;
for (kv in Trie.iter(t3)) {
sum += kv.1;
};
assert(sum == 24);
Class TrieMap<K, V>
provides a map from keys of type K
to values of type V
.
The class wraps and manipulates an underyling hash trie, found in the Trie
module. The trie is a binary tree in which the position of elements in the
tree are determined using the hash of the elements.
LIMITATIONS: This data structure allows at most MAX_LEAF_SIZE=8 hash collisions:
attempts to insert more than MAX_LEAF_SIZE keys (whether directly via put
or indirectly via other operations) with the same hash value will trap.
This limitation is inherited from the underlying Trie
data structure.
Note: The class
TrieMap
exposes the same interface as HashMap
.
Creating a map: The equality function is used to compare keys, and the hash function is used to hash keys. See the example below.
motoko name=initialize
import TrieMap "mo:base/TrieMap";
import Nat "mo:base/Nat";
import Hash "mo:base/Hash";
import Iter "mo:base/Iter";
let map = TrieMap.TrieMap<Nat, Nat>(Nat.equal, Hash.hash)
Functional set
Sets are partial maps from element type to unit type, i.e., the partial map represents the set with its domain.
LIMITATIONS: This data structure allows at most MAX_LEAF_SIZE=8 hash collisions:
attempts to insert more than MAX_LEAF_SIZE elements (whether directly via put
or indirectly via other operations) with the same hash value will trap.
This limitation is inherited from the underlying Trie
data structure.
Provides extended utility functions on Arrays.
Note the difference between mutable and non-mutable arrays below.
WARNING: If you are looking for a list that can grow and shrink in size, it is recommended you use either the Buffer class or the List class for those purposes. Arrays must be created with a fixed size.
Import from the base library to use this module.