There are programs you ship, and there are programs you write to get something done — a rename over a directory, a quick calculation, a check you will run twice and delete. Scripting languages own that second kind, and they own it for one reason: a single command runs the file.
mica --run gives Mica that command, without giving up what a compiler
knows. It ships with compiler release 6.12.5; every command below is exactly
what the released compiler does.
One command
cat > hello.mica << 'EOF'
program Hello;
imp
WriteLn : std;
begin
WriteLn("hello from a script");
end.
EOF
mica --run hello.micahello from a scriptThat is the whole ceremony. The compiler builds the file into a temporary directory at the debug tier, runs it, forwards its exit code, and removes every trace. Nothing appears beside your file, nothing is cached anywhere — a one-file debug compile is milliseconds, so the honest cache is no cache. The whole round trip above, compile and run, takes about a hundredth of a second.
Notice what did not appear: no banner, no progress lines. On a clean compile the compiler says nothing at all, so the program owns standard output and pipes stay clean. Warnings still surface — on standard error, where diagnostics belong.
Arguments and exit codes
A script earns its keep through its arguments and its exit code. Everything
after the file on the command line belongs to the program, readable through
the process unit:
cat > args.mica << 'EOF'
program Args;
imp
WriteLn : std;
* : process;
begin
WriteLn("count=%d", ArgCount());
WriteLn("first=%ls", Arg(1));
WriteLn("second=%ls", Arg(2));
end.
EOF
mica --run args.mica alpha betacount=2
first=alpha
second=betaWhen a program’s own arguments begin with a dash, put -- between file and
arguments; everything after it is passed through untouched.
The exit code works the way $? demands: the program’s verdict is the
command’s verdict. Even a runtime failure reports honestly — here a program
that prints one line and then indexes past an array’s bound, which trips
Mica’s always-on bounds guard:
cat > trap.mica << 'EOF'
program Trap;
imp
WriteLn : std;
type
Numbers = array[0..2] of int64;
var
values : Numbers;
index : int64;
begin
WriteLn("before the trap");
index := 5;
values[index] := 1;
end.
EOF
mica --run trap.mica; echo "exit=$?"before the trap
Mica runtime failure: reason=index_out_of_range (12), token_stream_index=40
Mica runtime context: file=.../trap.mica, line=16, column=11
Mica runtime source: values[index] := 1;
exit=1The file becomes a command
This is the part that surprises people meeting it for the first time: a Mica source file can be a command — indistinguishable from any installed program — and the mechanism behind it is worth two minutes of understanding, because it is the same mechanism behind every script on every Unix system.
What a shebang is. When you execute a file, the kernel reads its first
two bytes. If they are #!, it treats the rest of that first line as the
path of the program that should run this file, and starts that program with
the file’s path appended. That is the whole mechanism. It is a kernel
feature, not a language feature — it is how every bash and python3
script becomes runnable — and the line is called the shebang line.
#!/usr/bin/env mica adds one useful indirection: env looks up mica on
your PATH and runs it. That is the portable idiom scripting ecosystems
settled on, because the script then never hardcodes where the compiler is
installed. Most languages need the GNU-only env -S trick here, to smuggle
a flag like --run into the line; Mica does not, because the bare spelling
mica tool.mica already means “run this” — and the compiler’s source
preparation removes the #! line on every road, line numbers preserved, so
the same file compiles through --compile, runs through --run, and opens
in the editor without a diagnostic on line one.
A tool, step by step. Here is a complete command-line tool — sum, which
adds up whatever numbers follow it. Step one, write the file:
#!/usr/bin/env mica
{
sum — add up whatever numbers follow the command.
}
program Sum;
imp
WriteLn : std;
* : process;
Val : strings;
var
total, number : int64;
index : int32;
begin
total := 0;
{ every command-line argument is either a number that joins the total or a complaint on the way out }
for index := 1 to ArgCount() do
if Val(Arg(index), address number) then
total := total + number
else
WriteLn("not a number: %ls", Arg(index));
WriteLn("%lld", total);
end.Step two, make it executable — once:
chmod +x sum.micaStep three, it runs:
$ ./sum.mica 12 30
42Step four — the full command experience. Commands do not carry file
extensions, and the bare spelling accepts that too: any existing file counts
as the script. So drop the extension and move it onto your PATH:
$ cp sum.mica ~/bin/sum
$ sum 12 30 -5
37sum is now a command. Whoever runs it sees arguments in, answer out, exit
code honest — and a civil reply instead of a stack trace when an argument is
not a number:
$ sum 12 oops 30
not a number: oops
42What they never see is that there is no interpreter behind it. Each invocation compiles the file into a temporary directory and runs the result — about twenty milliseconds for this tool, compile and run together, beneath the threshold anyone notices — and every compile-time check ran before the first statement did. Your ten-line tool was type-checked, format-string checked, and bounds-guarded on every single invocation. No scripting language gives you that; no compiled language made it this easy before.
And a here-document is a program — --run - reads the source from standard
input:
mica --run - << 'EOF'
program Hi; imp WriteLn : std; begin WriteLn("hi from a here-document"); end.
EOFhi from a here-documentYour libraries come along
If your script lives inside a tree that carries a
mica.project, the run mode
discovers it exactly the way the editor does — the nearest project file up
the directory tree governs — and its encoding and contracts apply. A
script beside your project imports your libraries with no flags at all:
$ cat mica.project
{
"contracts": ["build-lib"]
}
$ mica --run twice.mica # twice.mica says: imp Twice : Numbers;
twice(21) = 42No import paths to configure, no environment to activate. The file your build and your editor already read is the whole configuration — and a here-document typed inside the tree gets the same facts.
The check happens before the run
Here is the part no scripting language gives you. Change args.mica to
print a string with %s — the utf-8 specifier — while the file compiles
under the utf-32 default:
9: WriteLn("first=%s", Arg(1));
^ analyzer error 5184 [9,25]: format string validation error: string format specifier at position 1 must use '%ls' for UTF-32 encoding (found '%s')The script never ran. Elsewhere the matching mistake waits until run time,
for the input that happens to reach the broken line; a Mica script fails on
your terminal, before a single statement executes, with the line and the
fix in the message. Every compile-time check — types, format
strings, unused variables, definite assignment — runs at full strength under
--run, because the default is the debug tier, where checks are maximal and
latency is already unmeasurable.
The mode is deliberately narrow about everything else. It targets the host
architecture, and it combines with exactly four flags — --optimize,
--stdlib, --memory-class, --tasking. Ask it for anything the build
owns and it refuses in one line:
mica --build somewhere --run hello.micathe flag '-build' cannot be combined with '--run': the run mode owns compilation, linking, and the build directory, and combines only with --optimize, --stdlib, --memory-class, and --tasking
run 'mica --help' for the compiler's full flag surfaceAnd when a script outgrows scripting — it happens — nothing needs
rewriting: the same file goes through --compile --link --optimize O2 and
becomes the shipped program it was slowly becoming anyway.
Where it came from
The run mode shipped with compiler release 6.12.5, together with the project model it composes with. The announcement article tells the story in release terms; the command-line reference states every rule.
Next
- The project file — the three lines that make a script see your libraries.
- The shape of a Mica program — part 2 of the series, if you arrived here directly.
- The starter repository — the container ships everything these pages run.