Syntax
Weir uses S-expression syntax from the Lisp family, extended with type annotations, collection literals, and modern ergonomics.
S-Expressions
Section titled âS-Expressionsâ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))Comments
Section titled âCommentsâLine comments start with ;:
;; This is a comment(+ 1 2) ;; inline commentDocstrings are the first expression in a definition body:
(defn add ((x : i32) (y : i32)) : i32 "Add two integers." (+ x y))Type Annotations
Section titled âType AnnotationsâInline (preferred)
Section titled âInline (preferred)â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) ...)Separate declarations
Section titled âSeparate declarationsâ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.
Function Types
Section titled âFunction Typesâ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-orderType Variables
Section titled âType VariablesâQuote-prefixed lowercase, OCaml-style: 'a, 'b, 'elem:
(deftype (Option 'a) (Some 'a) None)
(deftype (Result 'ok 'err) (Ok 'ok) (Err 'err))Keywords
Section titled âKeywordsâ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.
Collection Literals
Section titled âCollection Literalsâ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[]{}Numeric Types
Section titled âNumeric Typesâ| Category | Types |
|---|---|
| Signed integers | i8, i16, i32, i64 |
| Unsigned integers | u8, u16, u32, u64 |
| Floating point | f32, 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 ...)Operators
Section titled âOperatorsâ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 notString Formatting
Section titled âString Formattingâstr concatenates values into a string:
(str "Hello, " name "!") ;; => "Hello, World!"(str "x = " x ", y = " y) ;; => "x = 5, y = 10"Threading Macros
Section titled âThreading Macrosâ-> (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))Visibility
Section titled âVisibilityâDefinitions are private by default. Use pub to export:
(pub defn spawn-enemy (...) ...) ;; public(pub deftype Enemy ...) ;; public(defn internal-helper (...) ...) ;; privateModules and Imports
Section titled âModules and Importsâ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.
Comparison with Other Lisps
Section titled âComparison with Other Lispsâ| Feature | Common Lisp | Clojure | Weir |
|---|---|---|---|
| Types | Dynamic | Dynamic | Static (declarative) |
| Macros | Unhygienic | Limited | Hygienic |
| Mutation | Default | Discouraged | Explicit mut |
| Pattern matching | Via library | Via core.match | Built-in, exhaustive |
| Compilation | Native (SBCL) | JVM bytecode | Native (Cranelift) |
| Live reload | Image-based | REPL | Function-level hot-swap |
| Collections | () lists | [] {} #{} | () [] {} |