// An ordinary line comment. ///| /// A doc comment. `///|` above is the separator MoonBit's own formatter puts /// between top-level items — it is a comment too, and coloured as one. /// /// Everything below compiles. `moon check` reports no warnings and no errors, /// which is the point: a colouring demo that does not build is a screenshot. // ── literals ──────────────────────────────────────────────────────────────── ///| fn literals() -> Unit { // Strings, and the four other quoted forms. let plain = "an ordinary string" let escaped = "a quote \" and a backslash \\ and a newline \n" let unicode = "\u{1F31C} é" let bytes = b"\xDE\xAD\xBE\xEF" let ch = 'x' let escaped_char = '\n' let byte_char = b'A' // Interpolation is one string span, brace to brace: the expression inside is // deliberately not coloured as code. let name = "MoonBit" let interpolated = "hello \{name}, and \{1 + 2}" // The one line in this file the editor colours *wrongly*, kept on purpose. // A string nested inside an interpolation ends the outer literal as far as // the scanner is concerned, so `yes` and `no` below come out as identifiers // rather than as part of the string. The compiler disagrees, and is right; // finding the real end needs the parser. See reference/languages.md. let nested = "answer: \{if true { "yes" } else { "no" }}" // A multi-line string is a run of lines, each complete in itself. #| is // literal; $| interpolates. let raw = #| {"note": "braces and \backslashes are literal here"} #| second line let woven = $| the name again: \{name} $| and arithmetic: \{6 * 7} println(plain) println(escaped) println(unicode) println("bytes: \{bytes.length()} of them") println("chars: \{ch} \{escaped_char.to_int()} \{byte_char.to_int()}") println(interpolated) println(nested) println(raw) println(woven) } // ── numbers ───────────────────────────────────────────────────────────────── ///| /// Every numeric form the grammar allows, including the suffixes — which are /// upper case or they are not suffixes at all. fn numbers() -> Unit { let decimal = 1_000_000 let hexadecimal = 0xFF_FF let octal = 0o17 let binary = 0b1010_1010 let double = 1.5 let trailing_point = 1.0 let exponent = 1.5e-3 let hex_float = 0x1.8p3 let unsigned : UInt = 42U let long : Int64 = 42L let unsigned_long : UInt64 = 42UL let big : BigInt = 42N let single : Float = 1.0F // An integer ends before `..`, so this is 1, then ..=, then 5 — not the // double `1.` followed by `.=5`. let mut sum = 0 for i in 1..<=5 { sum = sum + i } for i in 0..<3 { sum = sum - i } println("\{decimal} \{hexadecimal} \{octal} \{binary}") println("\{double} \{trailing_point} \{exponent} \{hex_float}") println("\{unsigned} \{long} \{unsigned_long} \{big} \{single}") println("range sum: \{sum}") } // ── types, traits, and the case rule ──────────────────────────────────────── ///| /// A capitalised name can only be a type, a trait or a constructor — that is a /// lexical rule in MoonBit, not a convention, so the scanner needs no table of /// built-in type names. pub(all) struct Point { x : Int y : Int } derive(Eq, Debug) ///| pub(all) enum Shape { Dot Line(Point, Point) Poly(Array[Point]) } derive(Debug) ///| pub(open) trait Describe { fn describe(Self) -> String } ///| pub impl Describe for Point with fn describe(self) { "Point(\{self.x}, \{self.y})" } ///| pub impl Show for Point with fn output(self, logger) { logger.write_string(self.describe()) } ///| /// `extend` promotes a trait's method onto the type, so `p.describe()` works /// as well as `Describe::describe(p)`. extend Point with Describe::{describe} ///| /// A labelled argument with a default: the call site may leave it out. fn walk(path : Array[Point], closed? : Bool = false) -> Int { let steps = path.length() if closed { steps } else { steps - 1 } } // ── control flow ──────────────────────────────────────────────────────────── ///| /// Raised when a shape has no points to speak of. pub suberror Empty ///| fn corners(shape : Shape) -> Int raise Empty { match shape { Dot => 1 Line(_, _) => 2 Poly(points) => { guard points.length() > 0 else { raise Empty } points.length() } } } ///| fn control() -> Unit { let square : Array[Point] = [ { x: 0, y: 0, }, { x: 1, y: 0, }, { x: 1, y: 1, }, { x: 0, y: 1, }, ] println("open path steps: \{walk(square)}") println("closed path steps: \{walk(square, closed=true)}") for shape in [Dot, Line({ x: 0, y: 0, }, { x: 1, y: 1, }), Poly(square)] { debug(shape) println(" … has \{corners(shape)} corner(s)") catch { Empty => println(" … has no corners") } } // A `for` loop carrying accumulators, with `break` to return a value and // `continue` to go round again. (The older `loop (a, b) { … }` form is // deprecated; this is what replaced it.) let counted = for i = 0, acc = 0 { if i >= 10 { break acc } else if i % 2 == 0 { continue i + 1, acc + i } else { continue i + 1, acc } } println("even numbers below ten add to \{counted}") // while, with a mutable binding. let mut countdown = 3 while countdown > 0 { countdown = countdown - 1 } println("countdown reached \{countdown}") // Option and Result, and the four constructors the language's readers know. let found : Int? = Some(7) let missing : Int? = None let good : Result[Int, String] = Ok(1) let bad : Result[Int, String] = Err("nope") // Option and Result are printed with `debug`: interpolating them would go // through Show, which the toolchain now steers away from for debugging. debug(found) debug(missing) debug(good) debug(bad) println("truth values: \{true} \{false}") // A tuple, and its accessor. let pair = (1, "one") println("pair.0 = \{pair.0}, pair.1 = \{pair.1}") // The pipe operator. let piped = [3, 1, 2] |> sorted println("piped:") debug(piped) } ///| fn sorted(xs : Array[Int]) -> Array[Int] { let copy = xs.copy() copy.sort() copy } // ── attributes ────────────────────────────────────────────────────────────── ///| /// An attribute takes the whole line: after the dotted name, everything up to /// the newline is its raw payload. #deprecated("kept only so the colouring has something to show") pub fn old_name() -> Int { 1 } ///| /// A user-defined attribute has a namespace and is ignored by the compiler. #custom.note(kind="demo", enabled=true) fn annotated() -> Int { 2 } // ── the entry point ───────────────────────────────────────────────────────── ///| fn main { literals() println("") numbers() println("") control() println("") println("annotated() = \{annotated()}") }