Skip to content

MON Cookbook

Welcome to the MON Cookbook! This guide provides practical MON examples and MON patterns to help you solve common data structuring problems using MON. Think of it as a collection of recipes for writing clean, efficient, and powerful MON configurations, often providing more elegant solutions than traditional formats like JSON or TOML.

  1. Define your configuration keys and values. Use unquoted keys for simplicity and add comments (//) for notes. MON supports basic data types like String, Number, and Boolean.

    config.mon
    {
    // The name of our application
    app_name: "Awesome App",
    // Network settings
    host: "localhost",
    port: 8080,
    // Feature flags. MON accepts on/true and off/false for booleans.
    enable_feature_x: on,
    enable_https: false,
    // A list of admin users
    admins: ["alice", "bob"],
    }
  2. Review the result. The resulting configuration is clean, self-documenting, and easy for anyone on your team to read and edit.


Recipe 2: Avoiding Repetition (The DRY Principle)

Section titled “Recipe 2: Avoiding Repetition (The DRY Principle)”
  1. Create a reusable template with an Anchor (&). An anchor gives a nickname to any value (object, array, string, etc.), turning it into a template. The anchor itself doesn’t appear in the final output.

    {
    // The & makes `default_user` a template.
    &default_user: {
    theme: "dark",
    notifications: on,
    },
    }
  2. Make copies with an Alias (*). An alias makes a deep copy of an anchor. Each copy is completely separate.

    {
    &default_user: { theme: "dark" },
    // alice_settings is a perfect copy of the template
    alice_settings: *default_user,
    // bob_settings is another, totally independent copy
    bob_settings: *default_user,
    }
  3. Extend templates with a Spread (...*). A spread unpacks an anchor’s content into a new object or array, allowing you to extend it.

    For Objects: Spreads merge keys. If a key exists in both the template and your new object, the local value wins.

    {
    &base_config: {
    host: "localhost",
    log_level: "info",
    },
    prod_config: {
    ...*base_config, // 1. Inherit host and log_level
    host: "api.myapp.com", // 2. Override the host
    log_level: "error", // 3. Override the log_level
    },
    }

    For Arrays: Spreads combine elements.

    {
    &base_permissions: ["READ", "WRITE"],
    admin_permissions: [
    "LOGIN",
    ...*base_permissions, // Inserts "READ" and "WRITE" here
    "DELETE",
    ],
    }

  1. Define your shared data in a separate file. For example, create a schemas.mon file to hold your type definitions.

    schemas.mon

    {
    User: #struct { name(String) },
    Status: #enum { Active, Inactive },
    }
  2. Import the data into your main file. All import statements must appear at the top of the file, before the opening { of the root object.

    Namespace Imports (Safest): This bundles everything from another file into a single name, preventing conflicts.

    main.mon
    // Import everything from schemas.mon into the `schemas` name
    import * as schemas from "./schemas.mon"
    {
    // Access imported members using the namespace
    admin :: schemas.User = { name: "Admin" },
    current_status: $schemas.Status.Active,
    }

    Named Imports (Convenient): Import specific parts directly into your file’s scope.

    main.mon
    // Import only User and Status
    import { User, Status } from "./schemas.mon"
    {
    // Use them without a prefix
    guest :: User = { name: "Guest" },
    current_status: $Status.Inactive,
    }

  1. Define a Struct Blueprint. A struct defines what keys an object should have and what their value types should be.

    {
    // A blueprint for a Product object
    Product: #struct {
    id(String), // Required String field
    price(Number), // Required Number field
    in_stock(Boolean) = false, // Optional Boolean, defaults to false
    },
    }
  2. Validate Your Data with ::. Use the :: operator to validate an object against a struct. The MON compiler will report an error if the data doesn’t match the blueprint.

    {
    Product: #struct { id(String), price(Number) },
    // This object is valid and matches the Product struct
    my_book :: Product = {
    id: "978-0321765723",
    price: 55.99,
    },
    // INVALID: The compiler would throw an error here because `price` is a string.
    // my_widget :: Product = {
    // id: "widget-123",
    // price: "25.00",
    // },
    }