Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Hello World

This guide walks through creating, running, and compiling your first Milang program and explains common variants useful when learning the language.

Your First Program

Create a file called hello.mi with this content:

main world =
  world.io.println "Hello, Milang!"
Hello, Milang!

Run it with the bundled binary:

./milang run hello.mi

Expected output:

Hello, Milang!

What main and world mean

  • main is the program entry point by convention (not a language keyword).
  • world is an explicit record that carries runtime capabilities: world.io (console and file IO), world.process (exec/exit), world.argv, and helpers like getEnv.
  • Only code that receives the appropriate part of world can perform the corresponding effects — pass only what you need to follow the principle of least privilege.

Printing and Helpers

println appends a newline; print does not. Prefer small helpers that accept only the sub-record they need:

greet io name = io.println ("Hello, " + name + "!")

main world =
  greet world.io "Alice"
Hello, Alice!

This makes greet unable to access process or filesystem capabilities.

Handling Command-Line Arguments

A more advanced “Hello World” might greet someone by name, using command-line arguments. The world.argv list contains the arguments. The following example, which you can save as hello_argv.mi, demonstrates this. It uses a helper function to safely get an argument or fall back to a default value.

-- main entrypoint
main world =
  name = fromMaybe "World" (at' 1 world.argv)
  world.io.println ("Hello, " + name + "!")
unbound variable: fromMaybe

Run this from your terminal:

# With no arguments
./milang run hello_argv.mi
# Expected output: Hello, World!

# With an argument
./milang run hello_argv.mi "Universe"
# Expected output: Hello, Universe!

This example shows several concepts:

  • world.argv: A list of strings from the command line.
  • at': A prelude function to safely get an element from a list by index. It returns a Maybe value. (at' takes index first; at takes list first for use as an operator: xs `at` 1).
  • fromMaybe: A prelude function that unwraps a Maybe, returning a default value if Nothing.

This pattern of using helpers to safely extract information is common in Milang.

Script Mode (quick experiments)

When a file does not define main that takes a parameter, milang run executes in script mode: every top-level binding is evaluated and printed. This is ideal for short tests and REPL-style exploration.

x = 6 * 7
y = x + 1
build =  {target = c, os = linux, arch = x86_64}
x = 42
y = 43

Script-mode output prints name/value pairs for top-level bindings (prelude/internal bindings are hidden).

Printing non-strings and Maybe values

Use toString to render non-string values. Many standard library functions return Maybe to handle operations that might fail, like converting a string to a number. For example, toInt returns Just(number) on success and Nothing on failure.

Use toString to safely print these Maybe values.

main world =
  world.io.println (toString (toInt "42"))
  world.io.println (toString (toInt "abc"))
Just(42)
Nothing

This will print:

Just(42)
Nothing

The Maybe type is how Milang handles optional values, avoiding nulls and making error handling more explicit. You can use pattern matching to safely unwrap these values.

Compiling to C

Emit the generated C and compile it:

./milang compile hello.mi hello.c
gcc hello.c -o hello
./hello

The C file embeds the milang runtime; you only need a standard C toolchain.

Using the REPL

Start the REPL for interactive experimentation:

./milang repl

Example session:

> 2 + 3
5
> f x = x * x
> f 8
64
> map f [1, 2, 3, 4]
[1, 4, 9, 16]

Bindings persist across lines; you may rethink and refine definitions live. Many common functions like map, filter, and fold are available automatically because they are part of the prelude.

Next Steps