Skip to content

Quick Start

Welcome to Janus :core — the foundational profile that teaches systems programming with radical honesty. No magic, no hidden costs, just pure computational thinking.

Terminal window
# Clone the compiler
git clone https://git.sovereign-society.org/janus/janus-lang.git
cd janus-lang
# Build (requires Zig 0.16.x)
zig build
# Run your first program
./zig-out/bin/janus run examples/hello.jan

Create hello.jan:

func main() do
println("Hello, Monastery!")
end

Run it:

Terminal window
janus run hello.jan

What you learned:

  • func declares a function
  • main() is the entry point
  • println() prints to stdout (built-in)
func main() do
// Immutable binding (preferred)
let x = 42
let pi = 3.14159
let is_learning = true
// Mutable binding (when needed)
var count = 0
count = count + 1
println("Count: ")
print_int(count)
end

Types in :core:

TypeDescription
i32, i64Signed integers
f6464-bit floating point
boolBoolean (true / false)
voidNo return value
func check_sign(x: i32) do
if x > 0 do
println("Positive")
else if x < 0 do
println("Negative")
else
println("Zero")
end
end
func count_to_ten() do
for i in 0..10 do
print_int(i)
end
end
func describe_number(n: i32) do
match n {
0 => println("zero"),
1 => println("one"),
2 => println("two"),
_ => println("something else"),
}
end

::: tip SPEC-017 Law 2 match, enum, struct use { }. Control flow (func, if, for, while) uses do..end. This is law. :::

// Function with return value
func add(a: i32, b: i32) -> i32 do
return a + b
end
// Recursive function
func factorial(n: i32) -> i32 do
if n <= 1 do return 1 end
return n * factorial(n - 1)
end

All function signatures MUST have explicit types.

Janus uses Zig-style error unions — errors as values, not exceptions:

func divide(a: i64, b: i64) !i64 do
if b == 0 do
fail DivisionByZero
end
return a / b
end
func main() do
let result = divide(10, 0) catch |err| do
println("Error: division by zero")
0
end
print_int(result)
end

Keywords:

  • !T — error union return type
  • fail — return an error
  • catch — handle an error
  • try / ? — propagate an error

Janus compiles through Zig, but normal .jan code should still look like Janus. Prefer native Janus modules and generated wrappers first:

use std.collections as collections
func main() do
let numbers = collections.new_vector()
_ = numbers
end

Use use zig only at an explicit boundary:

  • bridge modules such as use zig "std/bridge/process_bridge.zig"
  • compiler-generated wrappers
  • narrow interop shims you intend to isolate and replace later

Raw use zig "std/..." across ordinary application modules is migration debt, not the target style.

In :core, memory is explicit whether it comes from a native Janus module or a bridge.

Golden rules:

  1. Every init needs a deinit
  2. Use defer for automatic cleanup
  3. No garbage collection, no magic

The :core profile intentionally excludes:

FeatureAvailable In
Concurrency (spawn, channels):service
Async/Await:service
Actors, supervision trees:cluster
GPU/NPU kernels:compute
Raw pointers, unsafe:sovereign

Why? To teach fundamentals without overwhelming complexity.

  • [Profiles System]/learn/profiles/ — Understand the capability ladder
  • [Why Janus?]/learn/introduction/ — The philosophy and design

“The Monastery teaches fundamentals. Master :core, understand all of Janus.”