Skip to content

Hello! 👋 My name is Nathan Weir. This is a fun personal project for using AI to build a bespoke, domain-specific programming language. It is not a serious, professional project. This site and the language itself are largely generated via Claude Code. If you find yourself programming with Weir, have fun - but use at your own risk!

Syntax

Weir uses S-expression syntax from the Lisp family, extended with type annotations, collection literals, and modern ergonomics.

Everything is an expression. Function calls, special forms, and definitions all use parenthesized prefix notation:

(+ 1 2) ;; function call
(if (> x 0) "positive" "non-positive") ;; conditional
(defn add ((x : i32) (y : i32)) : i32 ;; definition
(+ x y))

Line comments start with ;:

;; This is a comment
(+ 1 2) ;; inline comment

Docstrings are the first expression in a definition body:

(defn add ((x : i32) (y : i32)) : i32
"Add two integers."
(+ x y))

Parameters are annotated with (name : Type), return type follows the parameter list:

(defn add ((x : i32) (y : i32)) : i32
(+ x y))
(defn map ((f : (Fn ['a] 'b)) (xs : (List 'a))) : (List 'b)
...)

For complex signatures, use declare:

(declare transform
(=> (Functor 'f)
(Fn [(Fn ['a] 'b) ('f 'a)] ('f 'b))))
(defn transform (func container)
(map func container))

When both are present, they must agree — a mismatch is a compile error.

Carp-style Fn with brackets separating arguments from return type:

(Fn [i32 String] Bool) ;; takes i32 and String, returns Bool
(Fn [] Unit) ;; takes no args, returns Unit
(Fn [(List 'a) (Fn ['a] 'b)] (List 'b)) ;; higher-order

Quote-prefixed lowercase, OCaml-style: 'a, 'b, 'elem:

(deftype (Option 'a)
(Some 'a)
None)
(deftype (Result 'ok 'err)
(Ok 'ok)
(Err 'err))

Colon-prefixed symbols used as values (distinct from type variables):

{:name "Alice" :health 100}
(spawn-enemy :pos (Vec2 0.0 0.0) :health 50)

No ambiguity — :keyword is a value, 'a is a type variable. Different prefix, different context.

Square brackets for arrays/vectors, curly braces for maps:

;; Vector
[1 2 3 4 5]
["hello" "world"]
;; Map (keywords as keys)
{:name "Alice"
:health 100
:pos (Vec2 0.0 0.0)}
;; Empty
[]
{}
CategoryTypes
Signed integersi8, i16, i32, i64
Unsigned integersu8, u16, u32, u64
Floating pointf32, f64

Unadorned integer literals default to i64, float literals to f64. Use ann to disambiguate:

(let ((x 42)) ;; x : i64
((y 3.14)) ;; y : f64
((z (ann f32 3.14))) ;; z : f32
...)

Weir has no infix operators. All operations use prefix notation:

(+ 1 2) ;; addition (variadic)
(- 10 3) ;; subtraction
(* 2 3 4) ;; multiplication (variadic)
(/ 10 3) ;; division
(mod 10 3) ;; modulo
(= x y) ;; equality
(!= x y) ;; inequality
(< x y) ;; less than
(> x y) ;; greater than
(<= x y) ;; less than or equal
(>= x y) ;; greater than or equal
(and a b c) ;; logical and (variadic)
(or a b c) ;; logical or (variadic)
(not x) ;; logical not

str concatenates values into a string:

(str "Hello, " name "!") ;; => "Hello, World!"
(str "x = " x ", y = " y) ;; => "x = 5, y = 10"

-> (thread-first) inserts the previous result as the first argument:

(-> enemy .pos .x)
;; equivalent to: (.x (.pos enemy))
(-> world
.entities
(filter alive?)
(map .pos))

->> (thread-last) inserts as the last argument:

(->> (range 100)
(filter even?)
(map square)
(take 10))

Definitions are private by default. Use pub to export:

(pub defn spawn-enemy (...) ...) ;; public
(pub deftype Enemy ...) ;; public
(defn internal-helper (...) ...) ;; private

File = module. Imports are top-level only:

;; Import specific items
(import game.entities (Enemy spawn-enemy))
;; Import with alias
(import math.vec2 :as v)
;; then: (v.add a b)
;; Import everything (discouraged)
(import math.vec2 :all)

Circular imports are disallowed. The compiler builds a dependency DAG from imports and compiles in topological order.

FeatureCommon LispClojureWeir
TypesDynamicDynamicStatic (declarative)
MacrosUnhygienicLimitedHygienic
MutationDefaultDiscouragedExplicit mut
Pattern matchingVia libraryVia core.matchBuilt-in, exhaustive
CompilationNative (SBCL)JVM bytecodeNative (Cranelift)
Live reloadImage-basedREPLFunction-level hot-swap
Collections() lists[] {} #{}() [] {}