1. Special Forms
    1. def
    2. if
    3. do
    4. fn
    5. let*
    6. quote
    7. loop / recur
    8. try / catch / finally / throw
    9. defmacro
    10. spawn
    11. yield
    12. await
    13. >! /
  2. Callable Non-Functions
  3. Native Functions
    1. Arithmetic
    2. Comparison
    3. Collections
    4. Type Predicates
    5. String Functions
    6. I/O
    7. Utility
    8. Concurrency
    9. Atoms
    10. Metadata
  4. Module Libraries
    1. Tar Archives (beer.tar)
    2. Shell Execution (beer.shell)
    3. TCP Sockets (beer.tcp)
    4. UDP Sockets (beer.udp)
    5. Network REPL (beer.nrepl)
    6. Simple eval REPL (beer.nrepl.simple)
    7. JSON (beer.json)
    8. HTTP Server (beer.http)
    9. Actor System (beer.hive)
  5. Standard Library (lib/core.beer)
    1. Macros
    2. Numeric
    3. Function Utilities
    4. Sequence Functions
    5. Map Functions
    6. Sorting and Dedup
    7. Exception Helpers
  6. Special Vars
  7. Bytecode Metaprogramming
    1. disasm
    2. asm
    3. Use cases
  8. C Foreign Function Interface (beer.ffi)
    1. Low-level primitives
    2. High-level macros
    3. Library utilities
    4. Probe generator (beer.ffi.probe)
  9. Testing (beer.test)

Special Forms

These are primitive language constructs handled by the compiler.

def

Define a var in the current namespace.

(def x 42)
(def greeting "hello")

if

Conditional evaluation. Only evaluates the chosen branch.

(if (> x 0) "positive" "non-positive")

do

Evaluate expressions sequentially, return the last.

(do (println "hello") (println "world") 42)  ;=> 42

fn

Create an anonymous function. Optionally named for self-recursion.

(fn [x y] (+ x y))
(fn factorial [n] (if (< n 2) 1 (* n (factorial (- n 1)))))

let*

Lexical bindings (primitive form — prefer let macro).

(let* [x 10 y 20] (+ x y))  ;=> 30

quote

Prevent evaluation. Reader shorthand: 'form.

(quote (1 2 3))  ;=> (1 2 3)
'foo              ;=> foo

loop / recur

Structured tail-recursive iteration.

(loop [i 0 acc 0]
  (if (< i 10) (recur (+ i 1) (+ acc i)) acc))  ;=> 45

try / catch / finally / throw

Exception handling. Only maps can be thrown.

(try
  (throw {:type :oops :msg "bad"})
  (catch e (println (:msg e)))
  (finally (println "done")))

defmacro

Define a macro.

(defmacro unless [test & body]
  `(if (not ~test) (do ~@body)))

spawn

Create a new cooperative task.

(def t (spawn (+ 1 2)))
(await t)  ;=> 3

yield

Yield control to the scheduler.

(yield)

await

Wait for a task to complete and return its result.

(await (spawn (+ 1 2)))  ;=> 3

>! / <!

Send to / receive from a channel. Blocks the task if needed.

(def ch (chan 1))
(>! ch 42)
(<! ch)  ;=> 42

Callable Non-Functions

Keywords, hash maps, and vectors can be used in head (call) position, like Clojure's IFn:

;; Keyword as map lookup (1-2 args)
(:foo {:foo 42})          ;=> 42
(:missing {:a 1} "nope")  ;=> "nope"

;; Map as lookup function (1-2 args)
({:a 1 :b 2} :b)          ;=> 2
({:a 1} :missing "nope")   ;=> "nope"

;; Vector as index function (1 arg)
([10 20 30] 1)             ;=> 20

;; Works dynamically — keyword bound to a variable
(let [k :name] (k {:name "alice"}))  ;=> "alice"

;; Useful as higher-order functions
(map :age [{:age 30} {:age 25}])     ;=> (30 25)

Native Functions

Arithmetic

FunctionDescriptionExample
+Addition (variadic)(+ 1 2 3)6
-Subtraction / negation(- 10 3)7, (- 5)-5
*Multiplication (variadic)(* 2 3 4)24
/Division (returns float for non-exact)(/ 10 3)3.33333, (/ 6 2)3
modModulus (sign matches divisor)(mod -7 3)2
remRemainder (sign matches dividend)(rem -7 3)-1
quotTruncating integer division(quot 7 2)3

All arithmetic supports fixnum, bigint, and float with automatic promotion.

Comparison

FunctionDescriptionExample
=Equality (cross-type sequence equality)(= [1 2] '(1 2))true
<Less than (variadic, strictly increasing)(< 1 2 3)true
>Greater than (variadic, strictly decreasing)(> 3 2 1)true
<=Less than or equal(<= 1 1 2)true
>=Greater than or equal(>= 3 3 1)true

Collections

FunctionDescriptionExample
listCreate a list(list 1 2 3)(1 2 3)
vectorCreate a vector(vector 1 2 3)[1 2 3]
hash-mapCreate a map from key-value pairs(hash-map :a 1 :b 2){:a 1 :b 2}
consPrepend to a sequence(cons 0 '(1 2))(0 1 2)
firstFirst element (works on lists, vectors, strings)(first [1 2 3])1
restAll but first(rest [1 2 3])(2 3)
nthGet element by index(nth [10 20 30] 1)20
countNumber of elements(count [1 2 3])3
conjAdd to collection (lists prepend, vectors append)(conj [1 2] 3)[1 2 3]
empty?Test if empty(empty? [])true
getGet from map/vector with optional default(get {:a 1} :a)1
assocAssociate key-value pairs(assoc {:a 1} :b 2){:a 1 :b 2}
dissocRemove keys from map(dissoc {:a 1 :b 2} :b){:a 1}
keysMap keys as list(keys {:a 1 :b 2})(:a :b)
valsMap values as list(vals {:a 1 :b 2})(1 2)
reduce-kvReduce over map key-value pairs(reduce-kv (fn [acc k v] (+ acc v)) 0 {:a 1 :b 2})3
contains?Check key existence (maps and vector indices)(contains? {:a 1} :a)true
concatConcatenate sequences(concat [1 2] [3 4])(1 2 3 4)

Type Predicates

FunctionDescriptionExample
nil?Test for nil(nil? nil)true
number?Fixnum, bigint, or float(number? 3.14)true
int?Fixnum or bigint(int? 42)true
float?Float(float? 3.14)true
string?String(string? "hi")true
symbol?Symbol(symbol? 'foo)true
keyword?Keyword(keyword? :foo)true
char?Character(char? \a)true
list?List (cons or nil)(list? '(1 2))true
vector?Vector(vector? [1 2])true
map?Hash map(map? {:a 1})true
fn?Function(fn? +)true
stream?I/O stream(stream? *out*)true
task?Task(task? (spawn 1))true
channel?Channel(channel? (chan))true
atom?Atom(atom? (atom 0))true

String Functions

FunctionDescriptionExample
strConcatenate as string(str "hi" " " 42)"hi 42"
subsSubstring(subs "hello" 1 3)"el"
str/upper-caseUppercase(str/upper-case "hi")"HI"
str/lower-caseLowercase(str/lower-case "HI")"hi"
str/trimTrim whitespace(str/trim " hi ")"hi"
str/joinJoin sequence with separator(str/join ", " [1 2 3])"1, 2, 3"
str/splitSplit string(str/split "a,b,c" ",")["a" "b" "c"]
str/includes?Substring check(str/includes? "hello" "ell")true
str/starts-with?Prefix check(str/starts-with? "hello" "he")true
str/ends-with?Suffix check(str/ends-with? "hello" "lo")true
str/replaceReplace all occurrences(str/replace "aabb" "a" "x")"xxbb"

Strings also work as sequences with first, rest, nth, count, empty?, and map.

I/O

FunctionDescriptionExample
printPrint values (display mode)(print "x=" 42)
printlnPrint values with newline(println "hello")
pr-strReadable string representation(pr-str "hi")"\"hi\""
prnPrint readable with newline(prn {:a 1})
openOpen file stream(open "f.txt" :read)
closeClose stream(close s)
read-lineRead line from stream or stdin(read-line)
read-bytesRead n bytes from stream(read-bytes s 1024)
writeWrite string to stream(write *out* "hi")
flushFlush stream buffer(flush *out*)
slurpRead entire file as string(slurp "f.txt")
spitWrite string to file(spit "f.txt" "data")

spit supports :append true: (spit "f.txt" "more" :append true)

Utility

FunctionDescriptionExample
notBoolean negation(not false)true
symbolCreate symbol from string(symbol "foo")foo
gensymGenerate unique symbol(gensym "tmp")tmp42
typeType as keyword(type 42):fixnum
floatCoerce to float(float 3)3.0
intCoerce to fixnum (truncate)(int 3.7)3
applyApply function to arg list(apply + [1 2 3])6
macroexpand-1Expand macro once(macroexpand-1 '(when true 1))
macroexpandFully expand macros(macroexpand '(when true 1))
set-macro!Mark a var as a macro(set-macro! 'my-macro)
in-nsSwitch/create namespace(in-ns 'my.ns)
requireLoad namespace with optional alias(require 'foo.bar :as 'fb)
evalCompile and execute a form(eval '(+ 1 2))3
read-stringParse a string into a beerlang value (one form)(read-string "(+ 1 2)")(+ 1 2)
keywordCreate keyword from string(keyword "foo"):foo
nameGet name of symbol or keyword as string(name :foo)"foo"
loadLoad and execute a .beer file(load "path/to/file.beer")
ns-publicsList symbols defined in a namespace(ns-publics 'beer.core)

Concurrency

FunctionDescriptionExample
chanCreate channel (unbuffered or buffered)(chan), (chan 10)
close!Close channel(close! ch)
sleepSuspend the current task for n milliseconds(sleep 500)
task-watchCall callback with result when task completes(task-watch t (fn [v] (println v)))

Use special forms >!, <!, spawn, await, yield for task/channel operations.

Atoms

FunctionDescriptionExample
atomCreate an atom with initial value(atom 0)
deref / @Read current value(deref a), @a
reset!Set atom to new value, returns new value(reset! a 42)42
swap!Apply fn to current value (with optional extra args)(swap! a inc), (swap! a + 10)
compare-and-set!CAS: set new if current equals old(compare-and-set! a 0 1)true
atom?Test if value is an atom(atom? a)true

Metadata

FunctionDescriptionExample
metaGet metadata from a symbol's var or a function(meta 'foo){:doc "..."}
with-metaReturn function with new metadata(with-meta f {:tag "x"})
alter-meta!Apply fn to var's current metadata(alter-meta! 'foo assoc :added true)
docPrint documentation for a symbol (macro)(doc map)

defn and defmacro support an optional docstring after the name:

(defn greet "Greet a person by name." [name] (str "Hello, " name))
(doc greet)
;; -------------------------
;; greet
;;   Greet a person by name.
;; -------------------------

Module Libraries

Tar Archives (beer.tar)

(require 'beer.tar :as 'tar)
FunctionDescriptionExample
tar/listList entries in a tar file(tar/list "lib.tar")[{:name "a.beer" :size 42 :offset 512} ...]
tar/read-entryRead a file from a tar(tar/read-entry "lib.tar" "a.beer")"(ns ...)"
tar/createCreate a tar from a map(tar/create "out.tar" {"a.txt" "contents"})

Shell Execution (beer.shell)

(require 'beer.shell :as 'shell)
FunctionDescriptionExample
shell/execExecute a shell command (variadic args)(shell/exec "ls" "-la"){:exit 0 :out "..." :err ""}

TCP Sockets (beer.tcp)

(require 'beer.tcp :as 'tcp)
FunctionDescriptionExample
tcp/listenListen on a port(tcp/listen 8080)
tcp/acceptAccept a connection from a listener(tcp/accept listener)
tcp/connectConnect to host:port(tcp/connect "localhost" 8080)
tcp/local-portGet local port of a stream(tcp/local-port listener)

UDP Sockets (beer.udp)

(require 'beer.udp :as 'udp)
FunctionDescriptionExample
udp/socketCreate an unbound UDP socket(udp/socket)
udp/bindCreate a UDP socket bound to a port(udp/bind 9999), (udp/bind "0.0.0.0" 9999)
udp/sendSend a datagram to host:port(udp/send sock "127.0.0.1" 9999 "hi")
udp/recvReceive a datagram (blocks cooperatively)(udp/recv sock 65507){:data "hi" :host "127.0.0.1" :port 55324}
udp/local-portGet bound local port(udp/local-port sock)9999

udp/recv returns {:data string :host string :port int}. Use (close sock) to close.

Echo server example:

(require 'beer.udp :as 'udp)

(defn echo-server [port]
  (let [sock (udp/bind port)]
    (println "UDP echo on port" port)
    (loop []
      (let [pkt (udp/recv sock 65507)]
        (when pkt
          (udp/send sock (:host pkt) (:port pkt) (:data pkt))
          (recur))))))

(spawn (echo-server 9999))

Network REPL (beer.nrepl)

(require 'beer.nrepl)
(beer.nrepl/start! 7888)

Embeds a structured TCP REPL server into any running beerlang process. The protocol is EDN map messages, one per line, inspired by Clojure's nREPL. Multiple concurrent clients are supported.

Protocol:

Client → server   {:op "eval" :code "(+ 1 2)" :id "id-1"}
Server → client   {:id "id-1" :value "3"}
                  {:id "id-1" :status "done"}

Supported ops:

OpRequest fieldsResponse fields
"eval":code — source string:value (pr-str result) or :err
"doc":sym — symbol name string:doc (docstring) or :err
"describe":ops (list), :version
FunctionDescriptionExample
beer.nrepl/start!Start server on port; returns actual port bound(beer.nrepl/start! 7888)7888
beer.nrepl/stop!Stop the server(beer.nrepl/stop!)
beer.nrepl/clientsNumber of currently connected clients(beer.nrepl/clients)1

From the terminal:

printf '{"op" "eval" "code" "(+ 1 2)" "id" "1"}\n' | nc localhost 7888
# → {:id "1" :value "3"}
# → {:id "1" :status "done"}

Emacs integration (requires beerlang-repl.el):

KeyCommand
C-c C-jbeerlang-connect — prompts for host:port (default localhost:7888)
C-c C-qbeerlang-disconnect
C-x C-e, C-c C-c, C-c C-rEval commands — route to live process

Simple eval REPL (beer.nrepl.simple)

A minimal eval-over-TCP server for nc/telnet — send a form, get a result back.

(require 'beer.nrepl.simple)
(beer.nrepl.simple/start! 7889)
$ nc localhost 7889
(+ 1 2)
3
(map inc [1 2 3])
(2 3 4)

Both servers can run simultaneously — beer.nrepl on 7888 for Emacs, beer.nrepl.simple on 7889 for quick nc access.

JSON (beer.json)

(require 'beer.json :as 'json)
FunctionDescriptionExample
json/parseParse JSON string to beerlang data(json/parse "{\"a\":1}"){"a" 1}
json/emitSerialize beerlang data to JSON string(json/emit {:a 1})"{\"a\":1}"

HTTP Server (beer.http)

(require 'beer.http :as 'http)
FunctionDescriptionExample
http/run-serverStart HTTP server with handler(http/run-server handler {:port 8080})
http/wrap-content-typeMiddleware: set Content-Type header(http/wrap-content-type handler "text/html")

The handler receives {:method :path :headers :body} and returns {:status :headers :body}.

Actor System (beer.hive)

(require 'beer.hive :as 'hive)
FunctionDescriptionExample
hive/spawn-actorSpawn actor with handler and initial state(hive/spawn-actor handler {:count 0})
hive/sendSend async message to actor(hive/send pid {:type :inc})
hive/askSend message and await reply(hive/ask pid {:type :get})
hive/stopStop an actor(hive/stop pid)
hive/whereisLook up actor by registered name(hive/whereis :counter)
hive/supervisorCreate supervisor for child actors(hive/supervisor :one-for-one [...])

Actor handlers receive (state msg) and return {:state new-state} or {:state s :reply val} for ask. Pass {:name :some-name} in opts to register the actor.


Standard Library (lib/core.beer)

Macros

MacroDescriptionExample
defnDefine named function (optional docstring, multi-arity)(defn add "Add two nums" [a b] (+ a b))
docPrint documentation for a symbol(doc add)
whenConditional without else branch(when (> x 0) (println "pos"))
andShort-circuit logical AND(and true 42)42
orShort-circuit logical OR(or nil false 42)42
condMulti-branch conditional(cond (< x 0) "neg" (> x 0) "pos" :else "zero")
->Thread-first(-> 5 (- 3) (* 2))4
->>Thread-last(->> [1 2 3] (map inc) (filter odd?))(3)
letDestructuring let bindings(let [[a b] [1 2]] (+ a b))3
with-openAuto-close resource(with-open [f (open "x" :read)] (read-line f))
nsDeclare namespace with requires(ns my.lib (:require [other :as o]))
doseqIterate sequence for side effects(doseq [x [1 2 3]] (println x))

Numeric

FunctionDescriptionExample
incIncrement by 1(inc 5)6
decDecrement by 1(dec 5)4
zero?Test for zero(zero? 0)true
pos?Test positive(pos? 5)true
neg?Test negative(neg? -1)true
even?Test even(even? 4)true
odd?Test odd(odd? 3)true
maxMaximum of values(max 1 3 2)3
minMinimum of values(min 1 3 2)1
absAbsolute value(abs -5)5

Function Utilities

FunctionDescriptionExample
identityReturn argument unchanged(identity 42)42
constantlyReturn a function that always returns x((constantly 5) :any)5
complementNegate a predicate((complement even?) 3)true
compCompose functions((comp inc inc) 0)2
partialPartial application((partial + 10) 5)15
juxtApply multiple fns, return vector((juxt inc dec) 5)[6 4]

Sequence Functions

FunctionDescriptionExample
mapTransform each element(map inc [1 2 3])(2 3 4)
filterKeep elements matching predicate(filter even? [1 2 3 4])(2 4)
reduceFold sequence with function(reduce + 0 [1 2 3])6
secondSecond element(second [1 2 3])2
ffirstFirst of first(ffirst [[1 2] [3]])1
lastLast element(last [1 2 3])3
butlastAll but last(butlast [1 2 3])(1 2)
takeTake first n elements(take 2 [1 2 3])(1 2)
dropDrop first n elements(drop 2 [1 2 3])(3)
take-whileTake while predicate holds(take-while pos? [3 2 -1 4])(3 2)
drop-whileDrop while predicate holds(drop-while pos? [3 2 -1 4])(-1 4)
partitionSplit into groups of n(partition 2 [1 2 3 4])((1 2) (3 4))
rangeGenerate number range(range 5)(0 1 2 3 4)
repeatRepeat value n times(repeat 3 "x")("x" "x" "x")
repeatedlyCall fn n times(repeatedly 3 #(gensym))
reverseReverse sequence(reverse [1 2 3])(3 2 1)
intoPour elements into collection(into [] '(1 2 3))[1 2 3]
someFirst truthy predicate result(some even? [1 3 4])true
every?All match predicate(every? pos? [1 2 3])true
not-any?None match predicate(not-any? neg? [1 2 3])true
not-every?Not all match(not-every? even? [1 2])true
frequenciesCount occurrences(frequencies [:a :b :a]){:a 2 :b 1}
group-byGroup by function result(group-by even? [1 2 3 4]){false (1 3) true (2 4)}
mapcatMap then concat(mapcat reverse [[1 2] [3 4]])(2 1 4 3)
interleaveInterleave two sequences(interleave [:a :b] [1 2])(:a 1 :b 2)
zipmapZip keys and values into map(zipmap [:a :b] [1 2]){:a 1 :b 2}

Map Functions

FunctionDescriptionExample
get-inNested lookup(get-in {:a {:b 1}} [:a :b])1
assoc-inNested associate(assoc-in {} [:a :b] 1){:a {:b 1}}
updateUpdate value at key(update {:a 1} :a inc){:a 2}
update-inNested update(update-in {:a {:b 1}} [:a :b] inc){:a {:b 2}}
mergeMerge maps (last wins)(merge {:a 1} {:b 2}){:a 1 :b 2}
select-keysKeep only specified keys(select-keys {:a 1 :b 2 :c 3} [:a :c]){:a 1 :c 3}

Sorting and Dedup

FunctionDescriptionExample
sortSort sequence (merge sort)(sort [3 1 2])(1 2 3)
sort-bySort by key function(sort-by count ["aa" "b" "ccc"])("b" "aa" "ccc")
flattenFlatten nested sequences(flatten [[1 [2]] 3])(1 2 3)
distinctRemove duplicates(distinct [1 2 1 3])(1 2 3)

Exception Helpers

FunctionDescriptionExample
ex-infoCreate exception map(ex-info "oops" {:code 42}){:type :error :message "oops" :data {:code 42}}

Special Vars

VarDescription
*ns*Current namespace name (symbol)
*in*Standard input stream
*out*Standard output stream
*err*Standard error stream
*loaded-libs*Map of loaded namespace files
*load-path*Vector of library search paths (from BEERPATH env var + "lib/")

Bytecode Metaprogramming

disasm and asm expose the VM's bytecode representation as plain beerlang data, enabling inspection, transformation, and hand-crafted code generation.

disasm

Decompile a bytecode function into a data map:

(defn square [x] (* x x))
(disasm square)
;=> {:arity 1
;    :constants [*]
;    :code [[:ENTER 1] [:LOAD_LOCAL 0] [:LOAD_LOCAL 0]
;           [:LOAD_VAR 0] [:TAIL_CALL 2] [:RETURN]]}
  • :arity — number of fixed parameters (-1 for variadic)
  • :constants — constant pool (values referenced by index in :code)
  • :code — vector of [opcode-keyword operand ...] tuples

asm

Assemble a data map back into a callable function:

(def sq (asm (disasm (fn [x] (* x x)))))
(sq 7)   ;=> 49

The map must have :code, :constants, and :arity. Round-tripping (asm (disasm f)) produces an equivalent function.

Use cases

;; Inspect what the compiler generates
(disasm (fn [x] (if (> x 0) x (- x))))

;; Transform a function's bytecode
(let [d (disasm my-fn)]
  (asm (assoc d :code (transform (:code d)))))

C Foreign Function Interface (beer.ffi)

Requires make CFFI=1 at build time (links -lffi). Without it, beer.ffi is not registered.

(ns myscript (:require [beer.ffi :as ffi]))

Low-level primitives

FunctionSignatureDescription
ffi/open(ffi/open path) → cpointerLoad a shared library (dlopen)
ffi/sym(ffi/sym handle name) → cpointerLook up a symbol (dlsym)
ffi/close(ffi/close handle) → nilUnload library (dlclose)
ffi/call(ffi/call fn-ptr arg-types ret-type args) → valueCall a C function via libffi
ffi/malloc(ffi/malloc n) → cpointerAllocate n zero-initialised bytes
ffi/free(ffi/free ptr) → nilFree a ffi/malloc'd pointer
ffi/cget(ffi/cget ptr type offset) → valueRead a typed value from a raw pointer
ffi/cset!(ffi/cset! ptr type offset val) → nilWrite a typed value to a raw pointer
ffi/cpointer?(ffi/cpointer? x) → boolTrue if x is a cpointer
ffi/cnull?(ffi/cnull? ptr) → boolTrue if ptr is nil or NULL

Type keywords for ffi/call, ffi/cget, ffi/cset!:

KeywordC type
:voidvoid (return type only)
:boolint (0/1), marshalled to/from true/false
:int8 :uint8int8_t / uint8_t
:int16 :uint16int16_t / uint16_t
:int32 :uint32int32_t / uint32_t
:int64 :uint64int64_t / uint64_t
:floatfloat
:doubledouble
:pointervoid* — passed/returned as cpointer or nil
:stringchar* — Beerlang string copied to null-terminated C string for the call

High-level macros

Use these with the ffi/ alias (they live in beer.ffi, not beer.core).

ffi/def-cfn — bind a C function by name:

(ffi/def-cfn sqrt "m" "sqrt" [:double] :double)
(sqrt 2.0)   ;=> 1.41421...

Args: fn-name lib-short-name c-symbol arg-types ret-type

ffi/def-cstruct — name a struct descriptor map:

(ffi/def-cstruct Point {:size 16
                        :fields [{:name :x :type :double :offset 0}
                                 {:name :y :type :double :offset 8}]})

ffi/def-cstruct-accessors — generate helpers from a named struct:

(ffi/def-cstruct-accessors Point)
;; generates: point-x  set-point-x!  point-y  set-point-y!  point-alloc  point-size
(def p (point-alloc))
(set-point-x! p 3.0)
(point-x p)   ;=> 3.0
(ffi/free p)

ffi/load-bindings — install all fns and structs from a binding map:

(def libm (read-string (slurp "bindings/libm.beer")))
(ffi/load-bindings libm)
;; all :fns and :structs are now defined in the current namespace

Also accepts a literal map: (ffi/load-bindings {:fns [...] :structs []}).

Library utilities

FunctionDescription
ffi/lib-handleReturn a cached dlopen handle (opens on first call)
ffi/find-libraryResolve short name to platform path ("m""libm.dylib")
ffi/current-abiABI string for the current platform, e.g. "arm64-darwin"

Probe generator (beer.ffi.probe)

(ns myscript (:require [beer.ffi.probe :as probe]))
FunctionDescription
probe/generate-sourceGenerate a C probe source string from a spec map
probe/show-sourcePrint generated C without compiling (REPL helper)
probe/measureCompile and run a C probe; return measured struct layouts
probe/write-bindingsMeasure and write a binding file to a path

Spec map keys: :headers (list of #include names), :structs (list of {:name :fields}), :fns (verbatim function descriptors), :cc (compiler, default "cc"), :cflags (extra flags), :library, :version (for :meta).

Binding file format:

{:meta    {:library "zlib" :version "1.3.1" :abi "arm64-darwin" :cc "cc" ...}
 :fns     [{:name "compress" :lib "z" :sym "compress" :args [...] :ret :int32} ...]
 :structs {"arm64-darwin" [{:name "z_stream" :size 112 :fields [...]}]}}

:structs is keyed by ABI string, so a single binding file can cover multiple platforms.

beer-probe CLI:

beer-probe spec.beer bindings/libz.beer   # write binding file
beer-probe spec.beer                       # print measured layouts to stdout

Testing (beer.test)

(require 'beer.test :as 't)
Macro/FunctionDescriptionExample
deftestDefine a named test(t/deftest my-test (t/is (= 1 1)))
isAssert a condition(t/is (= 4 (+ 2 2)))
testingLabel a group of assertions(t/testing "addition" (t/is (= 2 (+ 1 1))))
run-testsRun all tests in current namespace(t/run-tests)

Copyright © 2026 Beerlang Contributors
Powered by Cryogen
Theme by KingMob