revo, the programming language

Lobsters Hottest Tools

Summary

Revo is a new programming language that features clean data flow without nesting, errors-as-values, everything as expressions, compile-time execution, and procedural macros.

<p><a href="https://lobste.rs/s/ikkx3r/revo_programming_language">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 06/02/26, 07:36 PM

# revo Source: https://gills.pages.dev/revo/ `` ⣄⠔⠄⡨⣀⣹⣥⣣⡚⣿⣓⣾⣫⣷⠮⡧⣬⣬⣑⢤⠤⡉⣿⡥⣂⢟⣕⡴⠬⠆⠸⡈⡆⠀⠀⠀⡋⠄⠂⠨⡆⠀⠠⠃⡀⠀⠀⠈⢰⣿ ⠩⣽⢟⢭⠶⢷⢵⣻⠿⢍⣟⡅⡧⡭⣽⡯⡯⣶⠭⣽⡢⠱⢿⢧⠓⢥⠨⠧⠉⣡⠐⠀⢑⣀⠂⠈⠆⡅⠀⡀⢥⠤⢁⠀⢣⠀⠀⢘⠽⡏ ⠬⡓⣼⢟⢜⡧⣷⣇⣧⡷⢯⣿⣿⣿⡶⣭⠍⣯⣻⢥⠞⣮⣬⣩⠅⢏⠆⠁⡀⠌⢩⡁⢁⠅⠡⢀⠠⠀⢉⢈⠀⠀⡅⠀⡇⠀⠀⠃⠭⣯ ⡎⣟⣵⠅⣾⢭⣿⣾⣯⣿⣿⢿⣿⣿⣿⣿⣧⡳⣥⢜⣟⡤⠉⣁⢧⢨⠌⡂⠀⠒⠬⡪⣊⠄⠀⠘⠀⠢⡁⠀⡀⠅⠅⠀⡄⠀⠈⠀⢕⡟ ⡕⣧⡗⠎⠷⣯⣿⣿⣷⣿⡻⢵⣿⣿⣿⣭⣿⡿⣟⣇⡧⣿⡭⢨⠫⠮⠄⠖⠀⠆⠦⠕⠁⠅⠀⠆⡁⠁⡀⠀⠄⡉⠁⠀⠄⡀⡂⠀⢼⣇ ⠃⣶⣯⠢⡗⣟⣿⡿⣿⣯⢿⣷⢾⢿⣛⣿⣿⣯⡻⣾⡾⣾⢵⣏⣩⠅⠅⠥⠀⠃⠠⠅⠃⠀⡈⠀⠭⠅⠀⠀⠁⠔⠀⠀⢂⠅⠅⠀⠽⡇ ⠱⣻⣻⡥⢫⢯⢿⣷⣷⣿⣿⢾⢿⣷⢯⣉⣟⣟⣾⣿⠳⣿⣇⣃⠯⠘⠔⡇⠒⠅⡖⠁⠄⠡⠑⣀⠀⡁⠀⡀⠃⠈⠀⠀⠅⠀⠀⠂⣋⡇ ⢂⠵⣓⣿⣍⢯⢯⡯⣟⣝⠷⣟⢝⣋⡏⣧⠯⣿⣿⢯⣼⣯⡂⡂⣟⡧⠐⣅⠂⠥⠏⠀⠂⡅⠅⡁⠁⠄⠠⠊⠀⠨⠀⢈⠀⠄⠉⢘⡟⡆ ⡥⣯⢌⡤⢽⣗⣏⠗⢿⣻⡭⡞⡷⣭⢿⡻⡯⡟⣕⣾⢽⢯⡭⠂⡍⠀⠏⠀⢌⠴⠂⠊⡄⢗⠂⠰⠀⠤⠣⠀⠀⠀⠁⠀⠀⠀⠅⠀⢕⡽ ⠵⡭⡩⠋⠽⣽⡛⡹⢯⠍⡛⡏⣯⢟⠟⠿⣩⣳⡿⡟⢗⣭⡤⡯⠁⡬⠠⠂⢄⠂⢤⠀⢖⠀⠀⢀⠂⢖⠀⠘⠀⠀⠂⠡⢤⠠⠀⠀⣫⡿ ⠀⢰⡇⢂⣃⠙⣧⡵⡙⠇⡷⠶⡷⠶⡟⡙⣩⡌⠯⡺⡍⡲⡸⣆⠼⠅⡱⠌⠡⠂⢄⣠⠃⠀⠀⡂⠈⠂⠠⢀⠀⠀⠀⠔⠀⠂⠂⠀⠨⣯ ⡔⠰⠼⣝⢪⢋⢋⠣⣧⡦⣦⡁⡭⠽⡛⠁⡫⢡⡄⠿⡫⣕⣔⢳⠌⢡⡸⠁⠁⠂⣠⡇⠀⠀⠀⠍⠈⠄⠀⠀⢀⠀⠔⠆⠁⠈⠠⠀⡹⡏ ⡖⣠⠄⠀⠯⢜⢨⠊⣒⢡⠨⡀⠢⢭⠴⡊⢕⠐⣄⡓⡾⠋⠗⠉⢀⠚⠀⢃⠂⠥⠲⠀⠀⠀⠄⠐⠀⠀⠐⠀⠁⠄⠱⠁⠊⠈⡀⠀⠊⣽ ⠀⡉⠒⣃⠰⡀⢁⠘⡉⠪⠋⡲⡅⠍⡎⡪⢣⠽⢭⠍⡥⠁⠂⡰⠠⣄⠥⠐⠀⠣⠄⢀⠐⠁⡀⠀⢐⠐⠀⠠⠁⠂⠄⠠⠀⠁⡀⠐⣫⡟ ⠐⢔⠁⠀⠬⠑⠗⠴⠀⠢⡁⢌⠅⢘⠓⡛⠈⡀⠄⠆⠠⠠⠢⢂⠕⠷⠈⢀⠈⡒⠄⠂⠀⠀⠈⠀⡀⠡⠈⠀⠆⡐⠀⠀⠓⠀⠀⠀⢐⢼ ⡀⠀⠈⠐⠠⠀⠀⠁⠁⠣⢀⡁⡄⠂⠔⠂⠂⢉⠁⡌⠄⠜⠈⠡⠐⠀⡐⠀⢨⠀⠁⠀⠀⡀⠍⠄⠠⠁⠀⠠⢄⠈⠀⠀⡄⠀⠀⡀⠒⣸ ⢘⠉⡒⠤⠌⢀⠑⠕⠀⠄⠄⠄⠀⠄⠑⠉⠨⠀⠄⠀⠂⠃⠀⠀⠈⠄⠄⠠⠈⠀⠀⡀⠠⠁⢠⠁⠁⠀⠐⠱⠀⠀⠀⡐⠀⠉⠄⠀⢸⠾ ⠀⠘⠈⡀⠈⠀⠈⠨⠐⠠⠠⠐⡀⡄⢠⠀⢀⠨⠀⠃⠀⠀⡁⡠⠀⠩⠀⠂⠁⠀⠀⠀⢒⡄⠊⠀⠀⢀⠈⠁⠀⠀⠀⠆⠘⠁⠄⠀⠄⣿ ⠄⡄⠀⠈⠠⠀⡀⠀⠀⠄⠀⠂⠀⠀⠄⠀⢀⠁⠁⢁⠁⠆⠀⠀⠀⠀⡀⠀⠈⠀⡀⠀⠱⠀⠀⠀⠀⠂⢄⠈⠀⠀⡀⡨⠠⡀⠠⠀⠄⡼ ⠠⠀⠁⠊⢀⠀⠠⠁⠁⠂⠂⡄⢄⠐⡀⢀⠊⠂⡀⡁⡀⢀⢀⠠⠀⠄⠂⠡⠠⠁⠀⠀⠀⠂⠀⢠⠒⠀⠀⠀⠐⠢⠀⢀⠀⠀⠄⠄⣿⣿ ⠁⠋⠙⠊⠐⠕⠁⡂⠈⠅⠠⠈⠐⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠀⠉⠀⠐⠀⠀⠀⠀⠀⣈⠀⠀⠀⠀⠈⡂⠈⠀⠀⠘⠌⢀⠥ ⠀⠀⠀⠀⠔⠔⠀⡀⡉⠀⠀⠀⠂⠠⠠⠀⠄⠄⠀⣀⢀⠀⡈⠀⠌⡐⠈⠈⠀⠀⠀⠀⠅⠀⣐⠉⠄⠀⠀⣂⠠⠁⡈⠀⠄⠄⢀⠣⢶⢿ ⠄⠀⠀⡁⠀⠄⠀⠀⠁⠈⠀⠈⠀⠁⠀⠁⡀⠁⠀⠁⠀⠀⠀⡀⠀⠀⠀⠀⠀⠠⡀⠑⠀⠀⠎⠀⢬⢠⠀⢂⠠⠢⠠⠈⡐⠸⠀⡐⣹⢷ ⠩⠥⠂⠆⡆⢠⠀⢄⠀⣀⡀⣄⠀⢈⠀⠀⣀⡀⡀⡀⠄⡄⠡⠀⠐⠀⠑⠀⠀⠁⠒⠒⠉⠡⡀⠈⠈⢀⠥⠈⠀⠀⠀⠀⠈⣞⣏⣿⡏⣿ ⠾⠝⠷⢿⣯⣿⠧⡧⠭⠣⠭⡭⡮⢭⠶⠶⠶⠦⣗⡶⠽⠭⣿⣶⠷⠵⠮⠽⠿⠮⠿⢷⡶⣖⡷⠲⠞⠷⠾⢿⢷⣿⣿⣿⣿⣯⢇⠟⡧⢈ `` ## pipes Clean data flow without nesting. Things flow from top to bottom. `` "hello!!" |> string.upper |> string.sub(0, 4) |> fn(s) s + ", world!" |> inspect # or with placeholders, "hello!!" |> _:upper() # obj:method() == obj.method(obj) |> _:sub(0, 4) |> do # you can even put any expression here # even do-end blocks _ + ", world!" end |> print # this is the same, but look at the order of operations print(log("hello":upper():sub(0, 4) + ", world!")) `` ## errors-as-values nil and booleans are replaced by atoms. You can’t use a value without handling an error – all crashes are explicit (WIP). Aided massively by pattern matching, `?`, `orelse`, and `:unwrap()`. `` # f might be # (:ok, "file-contents") # or (:err, :IoError) const f = fs.open({path = "./readme.md"}) # `?` unwraps :ok and panics on :err at top-level const f2 = fs.open({path = "./readme.md"})? # crashes if :err const f = fs.open({path = "./readme.md"}):unwrap() `` ## everything is an expression No statements – everything (really) always returns a value …but the code still looks procedural. `` let x = 10 # this line evaluates to 10 let label = if x > 0 "positive" else "zero" let a = let b = 5 # this whole line evaluates to 5 fn is_true() 5 + 5 == 10 # both x and is_true are the same function const x = fn is_true() do # do-end is one too # return and break are special return 5 + 5 == 10 end `` ## comp Execute any (really) expression at compile time. Any script can be compiled into bytecode and get any value baked in. `` revo -b script.rv revo script.rvo `` The compile-time VM does not differ from the runtime one. `` # asks for a line of input at build-time # then keeps the result at run-time const x = comp read() const long = do let t = 0 for x in 0..100 t += x t # similar to rust's {}, # revo do-end blocks return the last value end `` ## procedural macros Along with an AST-substituting macro system, This lets you just get an iterator over the raw AST tokens, run any code to transform them, then return back a table of the new AST. `` # > num, num, num -> Sigma^4_n=1(a * b + c) proc cmul!(iter) do print("inner: ", add3!(10,20,12)) print("peek: ", iter:peek()) match iter:peek() | (:number, n) => print("is number", n) | (other, n) => print(other, n) | x => print("not tuple: ", x) let a = 10 + (iter:next_of(:number)) let b = iter:next_of(:number) let c = iter:next_of(:number) let acc = 0 for i in 1..5 do acc += a * b + c end {(:number, acc)} end print(cmul!(10,20,30)) `` ## pattern matching Destructure and branch in place. You will be using atoms and tuples – they are beautiful solutions to their problems. `` match (:ok, 42) | (:ok, v) => v | (:err, e) => panic(e) | _ => panic() | _ => panic() # _ is wildcard, when nothing else matched # if you want to grab the actual value # , just put any binding name there const response = match "hello!" | "hello!" => "hi!" | x when (x:len() > 10) => "" | x when string?(x) => x + " to you too!" | _ => ":(" let f = match read({path = "./readme.md"}) | (:ok, file) => file | (:err, error) when error == :FileDNE => panic("file does not exist") | (error) => panic("error") | x => panic("unknown: ", x) `` ## fibers Make all your blocking code become non-blocking by just adding a spawn before it. `` fn serve(peer, message) do peer:send(message)? end while :true do # accept the next connection; if none is ready, this fiber parks until the # runtime sees a connection on the listening socket. let conn = server:accept()? # the only thing you have to do to make it async is to add `spawn` here! spawn serve(conn, port - 1) end `` ## tables Represent everything. Used for: - module exports - arrays - maps `` let t = {1, 2, 3, key = "value"} let rec = {name = "revo", version = 1} rec.name t[0] let mt = { name = fn(self) self.name, set_version = fn(self, v) self.version = v, DELTA = 0.0, } # a metatable is just a "table overlay" # which you can slap onto other tables set_metatable(rec, mt) let rec = {name = "revo", version = 2} set_metatable(rec2, mt) # name does not exist in rec or rec2, # but you can still call them assert_eq(rec:version(), 1) assert_eq(rec2:version(), 2) assert_eq(rec.DELTA, rec2.DELTA) `` ## convenient typing The type system is optional, but very well integrated. Untyped code works just fine, but typed code is faster and gets optimized better (and ensures code correctness at compile-time!). Most of your code will be inferred automatically. `` type Result = (:ok, any) | (:err, atom) struct User { name: string = "me", age: int = 21, fn get_age(self) -> Result (:ok, self.age), } # type is inferred let user = User{} print(user:get_age()) `` ## first-class tests They’re just closures, and they fail when you return an error. The `?` postfix operator propagates errors, giving you a pretty simple experience. `` fn add(a, b) a + b suite "add" do test "addition" do expect(add(20, 22) == 42)? expect(add(20, 22) != 22)? end test/skip "adds two tables" do expect(add({1,2}, {3, 4}) == {4,6})? end end `` ## quick start The only dependency is [zig](https://ziglang.org/) version 0.16.0. The repository is also available on [Codeberg](https://codeberg.org/lung/revo). `` cd /tmp git clone https://github.com/if-not-nil/revo zig build -Doptimize=ReleaseFast # static build with vendored ./zig-out/bin/revo -e 'print("hello " + "world")' # then, put that binary in your path cp ./zig-out/bin/revo ~/.local/bin/revo `` ## usage Please check `revo -h` first. `` revo script.rv # run script revo # start repl revo -e "1 + 2" # inline code revo -b script.rv # script.rv -> script.rvo revo -b -o output.rvo script # custom output path revo --bench1 script.rv # benchmarks script.rv revo --test script.rv # runs with test blocks revo --dis script.rv # bytecode disassembly `` ## lsp See [./lsp](https://gills.pages.dev/revo/lsp).

Similar Articles

Reve 2.0

Product Hunt

Reve 2.0 is a product that enables generating and editing 4K images with layout-based control.

Recursi

Product Hunt

Recursi is a self-improving vibe coding environment that operates without API fees.

Performance of the Wren programming language

Lobsters Hottest

The Wren language site posts micro-benchmarks showing it outruns standard Python/Ruby/Lua interpreters while staying a simple bytecode VM, crediting NaN-tagging and fixed object layout.