Odin Interoperability#
Kvist can use Odin packages directly. There is no wrapper layer:
(import os "core:os")
(import time "core:time")
(import sha2 "core:crypto/sha2")
(import raylib "vendor:raylib")
Procedures, types, constants, enums, multiple return values, allocators, and errors keep their Odin semantics. Kvist only changes the source syntax.
Core Packages#
Odin's core library gives Kvist programs a broad standard library:
core:os,core:path/filepath, andcore:timefor files, processes, paths, and time;core:strings,core:strconv,core:text/*, andcore:unicodefor text;core:encoding/*for JSON, CSV, INI, XML, Base64, hex, UUIDs, and more;core:container/*,core:slice, andcore:sortfor concrete collections;core:math,core:math/linalg, andcore:math/randfor numeric work;core:thread,core:sync, andcore:sync/chanfor concurrency;core:crypto/*,core:compress/*, andcore:image/*for common formats and algorithms;core:io,core:net,core:mem,core:reflect, andcore:testingfor lower-level application work.
Browse the complete Odin core library.
Vendor Packages#
Odin also ships maintained bindings and libraries under vendor:*, including
raylib, SDL, GLFW, OpenGL, Vulkan, WebGPU, Box2D, miniaudio, curl, Lua, stb,
and CommonMark.
Browse the complete Odin vendor library.
Calling Odin#
Call imported procedures normally:
(let [[data err] (os.read_entire_file path context.allocator)]
(if (= err nil)
(do
(defer (delete data))
(count data))
0))
Use (type ...) for polymorphic Odin types when needed:
(import queue "core:container/queue")
(let [q: (type queue.Queue int) {}]
(let [err (queue.init (addr q))]
(if (= err nil)
(do
(defer (queue.destroy (addr q)))
(queue.push-back (addr q) 42)))))
Odin APIs keep their ownership contracts. Delete owned strings, slices, maps, and containers; free owned pointers; do not delete borrowed views.
Interactive Odin Development#
The Kvist REPL can act as a typed development surface over Odin. Start it with a normal Kvist context file:
./kvist repl examples/interop/repl-odin.kvist
For a small amount of Odin, place it inside a typed Kvist definition:
(defn odin-sum-squares [limit: int] -> int
(odin "
total := 0
for i in 0..<limit {
total += i * i
}
return total
"))
The Kvist signature is the durable boundary: the definition persists, can be redefined, and can be called and inspected normally. The Odin body may also call typed Kvist procedures by their generated Odin names.
For a program containing substantial Odin, keep it as ordinary source:
project/
dev/user.kvist
native/
engine.odin
storage.odin
types.odin
The development context imports the package directory and optionally adds thin typed helpers:
(package dev)
(import native "../native")
(defn run-engine [iterations: int] -> int
(native.run iterations))
This allows the application to remain mostly Odin while Kvist supplies the
interactive development layer. A raw (odin "...") submitted by itself is a
single experiment; it does not add persistent top-level Odin declarations to
the session. Reusable declarations belong in a typed Kvist definition or an
.odin file, and a clean build remains the final program.
See the runnable REPL/Odin example, including its Odin-only support package.
Mixed Packages#
Kvist and Odin files can live in the same package directory and refer to each
other. Use ordinary imports for other Odin packages. Use foreign-import for
native libraries and (odin "...") only when raw Odin syntax is necessary.
Examples#
The interop examples cover:
- interactive typed boundaries around embedded and imported Odin;
- files, paths, time, strings, parsing, and encodings;
- queues, slices, matrices, SIMD, threads, channels, and synchronization;
- cryptography;
- raylib and stb vendor packages;
- foreign imports and Odin directives.