Motoko Programming Language

repository·master·Indexed 20 days ago

https://github.com/caffeinelabs/motoko

A high-level, actor-based programming language designed for developing smart contracts (canisters) on the Internet Computer (ICP) blockchain. It features a syntax similar to TypeScript/Java and is optimized for Wasm and asynchronous messaging. The documentation covers the actor model, type system, Motoko Runtime System (RTS) build process, and development tools including the moc compiler, mo-ld linker, and Nix-based build environment.

Tokens
140.4K
Snippets
408
Records
590
Agent score
70%

What's inside Motoko

  1. What is Motoko?

    master

    Motoko is a high-level programming language designed for building backends on the Internet Computer (ICP). It is optimized for AI agents and developers by providing platform-native features that simplify canister development.

    Core Concepts

    • Actor Model: Every Motoko canister is an actor—an isolated unit of state and behavior. Actors communicate via asynchronous messages, mapping directly to the ICP canister model.
    • Orthogonal Persistence: Variables declared in a persistent actor automatically survive canister upgrades. This eliminates the need for manual database layers, serialization, or upgrade hooks for most use cases.
    • Async/Await Messaging: Inter-canister communication uses async/await syntax, allowing complex asynchronous message flows to be written as if they were synchronous.
    • Strong Typing: The language features a sound type system including generics, variant types, pattern matching, and option types (?T) to prevent null-pointer errors.
    • WebAssembly Compilation: Motoko compiles directly to Wasm, handling ICP-specific requirements like Candid serialization and system API bindings automatically.
  2. Core features of Motoko on the Internet Computer

    master

    Motoko is designed specifically for the Internet Computer (IC) and includes built-in support for several IC-specific mechanisms:

    • Actors: Canisters are represented as actors with asynchronous and atomic methods.
    • Candid Integration: Automatic integration for de/serialising message arguments and automatic derivation of Candid interfaces.
    • Async/Await: Support for straight-line coding of asynchronous messaging patterns.
    • Orthogonal Persistence: Program state is automatically kept alive across messages.
    • Stable Variables: Allows persisting selected program state even across program version upgrades.
    • IC Mechanisms: Built-in support for cycles, upgrades, and heartbeats.
  3. Understand Motoko primitive types

    master

    Motoko's computations are built on several primitive types, including numeric types (integers and naturals), characters and text, booleans, and floating-point numbers. While common arithmetic uses built-in operators like + and -, more specialized operations are provided by core libraries (e.g., Int.toText for converting integers to strings).

    import Int "mo:core/Int";
    Int.toText(0); // returns "0"
  4. What is a `Region`?

    master

    A Region is an isolated chunk of stable memory that can be allocated, grown, and managed independently. It acts as a dedicated section of storage where contents are separate from the rest of the program.

    Key Characteristics

    • Manual Management: Unlike Motoko's native heap, memory management within a Region is manual. You must explicitly track byte offsets and space usage.
    • Pages: The fundamental unit of allocation. Each page is exactly 64 KiB (65,536 bytes) and is zero-initialized.
    • Blocks: The physical unit used by the ICP runtime. A block consists of 128 stable memory pages. While you allocate in pages, the system reserves memory in these larger block increments to optimize resource management.
    • Offsets: A specific byte position within a Region starting from 0. You calculate positions using current_position + bytes_used.
  5. What is a module in Motoko?

    master

    In Motoko, a module is a collection of related types, values, and functions grouped under a single namespace. Modules are primarily used to build libraries (like the core package or those from Mops) because they provide encapsulation and structure without the side effects associated with actors or objects.

    A module can define:

    • Public types.
    • Public functions (both synchronous and asynchronous).
    • Private types and internal logic (not exposed outside the namespace).
    • Nested modules.
  6. How actors expose interfaces via Candid

    master

    A Motoko actor presents its interface as a suite of named functions (methods) with defined argument and return types. When compiled, this interface is automatically translated into Candid, an interface description language. This allows the actor's interface to be consumed by other canisters, even those written in different languages like Rust.

    For the Main actor example provided previously, the corresponding Candid interface is:

    service : {
      greet : (text) -> (text);
      readCount : () -> (nat) query;
    }
  7. Define and use recursive types

    master

    Recursive types allow a type to refer to itself, which is essential for creating nested structures like linked lists.

    A common pattern is defining a list as an optional type containing a head value and a tail which is the list itself.

    • type List = ?(Nat, List); (Non-parameterized)
    • type List<T> = ?(T, List<T>); (Parameterized/Generic)
    type List<T> = ?(T, List<T>);
    
    // Example: A list of naturals: 1 -> 2 -> 3
    let numbers : List<Nat> = ?(1, ?(2, ?(3, null)));
  8. Understand Wasm data types and state

    master

    When developing for Wasm-based environments like Motoko, it is important to distinguish between the two types of data and the three forms of mutable state available in Wasm:

    Data Types

    • Numerics (e.g., int32/64, float32/64): Transparent data where the bit pattern is observable. These can be stored directly in Memory.
    • References (e.g., anyref, funcref): Opaque data (usually pointers) whose representation is not exposed. These can be stored in Tables but not directly in Memory.

    Mutable State

    • Globals: Stores a single value; can be mutated if defined as mutable.
    • Memory: An array of raw bytes that can be mutated and grown; used for numeric values.
    • Tables: An array of references that can be mutated and grown; used for reference values.
  9. Understand the difference between Wasm memory and stable memory

    master

    Canisters utilize two distinct storage types:

    1. Wasm memory (Heap memory): Automatically used for heap-allocated objects. It has a size limit (4 GiB or 6 GiB depending on 32-bit or 64-bit heap storage). Crucially, Wasm memory is cleared during canister upgrades, retaining only data stored in stable variables.
    2. Stable memory: Has a much larger maximum size (up to 500 GiB) and is preserved across canister upgrades.

    Memory modifications (both Wasm and stable) are committed only after a message execution succeeds. If execution fails, changes are not committed.

  10. Choose between Classical and Enhanced Orthogonal Persistence

    master

    Motoko supports two distinct persistence modes that determine how memory is handled during canister upgrades. Choosing the right mode is critical for scalability and memory management.

    Classical Persistence (Default)

    • Mechanism: Uses 32-bit memory and relies on Candid-based stabilization to move data to stable memory during upgrades.
    • Use Case: Traditional Motoko development.
    • Limitations: Can suffer from severe scalability issues during upgrades. Large amounts of stable data may exceed the upgrade instruction limit, potentially causing exponential duplication or stack overflows depending on your data structures.

    Enhanced Orthogonal Persistence (New)

    • Mechanism: Uses 64-bit main memory that is retained across upgrades without requiring stabilization to stable memory.
    • Use Case: High-scale applications requiring efficient upgrades.
    • Status: Intended to become the future default; classical persistence is being deprecated in favor of this mode.
  11. Understand Actor declaration constraints

    master

    When declaring an actor, there are specific constraints on its public interface and how it is instantiated:

    • Public Interface: All public fields must be non-var immutable shared functions. The public interface of an actor can only provide asynchronous messaging via shared functions.
    • Asynchronous Construction: Because actor construction is asynchronous, an actor declaration can only occur in an asynchronous context, such as the body of a non-query shared function, an async expression, or an async* expression.
    • Concurrency Warning: Actor declaration is implicitly asynchronous. The state of the enclosing actor may change due to concurrent processing of other incoming actor messages. You must guard against non-synchronized state changes.