What’s Rye? A high-level programming language designed for fluid, expressive coding.
How it’s different? Code is data, no-keywords, all functions, 41 datatypes, multi-lang, flexible language - strict about state
Rye console demos
Tables •
Exploring JSON •
HN / Somafm in console •
Testing vectors •
TODO in a REPL •
Per projectPer builds •
Eyr dialect
Rye is a homoiconic, dynamic language inspired by Rebol, with influences from Factor, Linux shell, and Go. Still in active development, our goal is to make it practical and powerful ASAP.
Written in Go, Rye doubles as a scripting companion for Go programs. Its libraries integrate seamlessly, and Rye can be embedded as a scripting or config layer.
As a high-level language, Rye explores bridging coding and user interfaces. Its interactive console (REPL) features syntax highlighting, history, tab completion, and tools to explore the language and environment you’re crafting.
Short Rye examples
"Hello World" .replace "World" "Mars" |print
; prints "Hello Mars"
"12 8 12 16 8 6" .load .unique .sum
; returns 42
switch 'cow { dog { "woof" } cow { "mooo" } cat { "meow" } }
; returns mooo
regexp "([0-9][a-z])" |submatch? "-7--x3--0k-r--"
; returns 0k
{ "Anne" "Joan" "Adam" } |filter { .first = "A" }
|for { .print }
IO, Spreadsheet, HTTP client
read\lines %data.txt |with
{ .length? .print , .tail 5 |print }
load\csv %ryebots.csv |where-equal 'level 7
|column? 'score |avg
get https://ryelang.org |write* %page.html
|reader |parse-html { <a> [ .attr? 'href |print ] }
go does { get https://httpbin.org/get |parse-json
-> "headers" -> "User-Agent" |print } print "Loading ..."
HTTP, SMTP server
http-server ":8082"
|handle "/" new-static-handler %public_html
|serve
http-server ":8081"
|handle "/time" fn { w r } { .write to-string now }
|serve
handler: fn { mail from to origin } {
printv from "new mail from {}" }
smtp-server ":25" |serve ?handler "demo"
Dialects
dict { name: "anakin" } |validate {
name: required calc { .capitalize }
score: optional 0 integer }
; returns { name: "Anakin" score: 0 }
; Rye like Rebol or Lisp has no operator precedence.
; But it has Math dialect which has it and more.
math { 2 + 2 * sqrt ( 12 + ( 24 / 3 ) ) }
; returns 42
rye .args .first :id
open sqlite://data.db
|query { select * from operator where id = ?id }
GUI
do\in fyne {
lab: label "I'm Waiting ..."
btn: button "Click here" does { lab .set-text "Finally ..." }
box: v-box [ lab layout-spacer btn ]
with app .window "Button" {
.resize size 200.0 100.0 ,
.set-content box ,
.show-and-run
}
}
do\in fyne {
app .window "Percentage Clock" :win
cont: v-box [
label "This Year [days]" :ly
progress-bar :py
label "This month [days]"
progress-bar :pM
label "Today [hours]"
progress-bar :ph
label "This hour [minutes]"
progress-bar :pm
label "This minute [seconds]"
progress-bar :ps
]
m-of: ?multiple-of
is-leap-year: fn { y } { all { y .m-of 4 not y .m-of 100 not y .m-of 400 } }
days-in: fn { y } { .is-leap-year .either { 366 } { 365 } }
go fn\par { } current {
forever {
with n:: now {
.year? ::y |concat* "Year " |set-text* ly ,
.year-day? / days-in y |set-value* py ,
.day? / days-in-month? n |set-value* pM ,
.hour? / 24 |set-value* ph ,
.minute? / 60 |set-value* pm ,
.second? / 60 |set-value* ps
sleep 500
}
}
}
win |resize size 300.0 200.0 |set-content cont |show-and-run
}
If examples above made you interested, you can delve deeper into the Rye language and Rye runtime. You have two sources for that, both are still work-in-progress, so check back later for more and better content:
Meet Rye | Focuses on Rye language, it's basics, concepts and components |
Rye Cookbook | Focuses on practical uses and specific technologies |
Function reference | Basic documentation and simple examples for many core functions |
Rye can run on Linux, Mac OS or Windows. Rye also runs in a web-browser (Wasm) and Docker. Rye could be compiled for mobile (Android and iOS).
You can try a browser-based Rye console right now by clicking a button on top-right of this page.
x86 64 | arm 64 | wasm | |
---|---|---|---|
Linux | v0.0.80 | ||
Windows | v0.0.80 | ||
Mac OS | v0.0.80 | v0.0.80 | |
/ | v0.0.80 |
Homebrew | brew install ryelang |
Docker | docker pull ghcr.io/refaktor/rye:latest |
Rye lives on GitHub—visit, star, report issues, or contribute! Building from source is easy with Go; see the README for instructions..
Rye's main binary comes with core language functions and also a lot of bindings and technologies already included. Below are some examples of what's already included:
AWS Bcrypt BSON Crypto FT search Goroutines HTML parser HTTP servers JSON MySQL Psql Postmark Regexp SMTP server SXML SQLite
Rye can be extended internally or externally. Two bigger external extension are developed in their own repositories:
Fyne GUI framework | Rye-fyne repository | Download: Linux MacOS Windows |
Gio UI framework | Rye-gio repository | |
Ebitengine game engine | Rye-ebitengine repository | |
Ryegen | Ryegen repository |
Github Blog Reddit Old blog Asciinema Youtube
Rye is still evolving. The basic ideas are formed, but we are still exploring, so your feedback is welcome!
All constructs (even control flow, print, assignments) return a value. Expression based code composes better and requires less state handling. And it's easier to write in Rye.
b: print 123 + a: print 123
; prints:
; 123
; 234 and assigns 123 to a and 234 to b
direction: 'out
print2 either direction = 'in { "Hello" } { "Bye" } ", mr. Jones"
; prints:
; Bye, mr. Jones
load\csv %bears.csv |column? 'weight |with { .avg .print , .max .print }
; prints:
; 214
; 749
Rye code consists of Rye values (e.g., blocks, words, literal values, ...). There is no difference between Rye code and Rye data. This brings internal consistency and options for code introspection.
thats-true: { print "That's true" }
do thats-true
; prints That's true
if 1 > 0 thats-true
; prints That's true
person: context { name: "Joe" }
print-name: fn\in { } person { print name }
; prints Joe
Words, functions, blocks of code, scopes (contexts), and literals are all values that can be created, passed, returned, or assigned. Every value of Rye Runtime is also accessible to the language itself.
some-word: 'this-word
some-func: fn { x } { x + 1 }
inspect-fn: fn { f } {
print2 "Code: " dump ?f
print2 "Result f(100): " f 100
}
inspect-fn ?some-func
; prints:
; Code: fn { x } { x + 1 }
; Result f(100): 101
some-scope: context { name: "Joe" }
print-name: fn\in { } some-scope { print name }
; prints Joe
Rye has no keywords and no special forms. For example if, loop, fn, try, return are all built-in functions and every active component of the language is just a built-in or an ordinary function call.
probe ?if
; Prints:
; [Pure BFunction(2): Executes a block of code if the condition is true, returning the result of the block or false.]
probe ?fn
; Prints:
; [Pure BFunction(2): Creates a function with named parameters specified in the first block and code in the second block.]
; our custom if
if-joe: fn { n code } { if n = "Joe" { do code } }
if-joe "Joe" { print "Hey" }
; prints Hey
flip-flop-loop: fn { n a b } { .loop { .is-odd .either { a } { b } |do } }
flip-flop-loop 5 { prns "Tik" } { prns "Tok" }
; Prints:
; Tik Tok Tik Tok Tik
Words you define with set-words are all constants and can't be redefined in given context. This being the default gives you certainty that by default things won't change under your feet.
name: "Tango"
name: "Cash"
; produces error, you can't use a set-word (one colon) to set an already set word
name:: "Cash"
; also produces error, you can't use a mod-word (two colons) to modify a constant
; you need to use var, or mod-word directly to define or modify a variable word
var 'current-month "January"
current-month:: "February"
current-year:: 2024 ; mod-word also creates a variable word if not yet created
current-year:: 2025
cant-redefine: fn { } { "me" }
As we've demonstrated above, set-words can only set words once. Modification of a word is visually explicit, and somewhat visually painfull, so you only use it when you have concrete reason.
{ "jim" "jane" "oto" } .for { :name , print capitalize name }
; produces error, set-word can't set an already set word
; sometimes you must modify a value of the word
{ "jim" "jane" "oto" } .for { ::name , print capitalize name }
; prints:
; Jim
; Jane
; Oto
; but also a lot of those cases can be solved better
{ "jim" "jane" "oto" } .for { .capitalize .print }
; prints:
; Jim
; Jane
; Oto
Rye code usually always returns a new value rather than modify a value and it has good support for this way of solving problems. For rare and specific cases where you do need to chanve a value in-place, the functions it has must end with exclamation mark (!).
; many cases where you would use append have better solutions
names: { "jane" "anne" "john" }
; you don't want to do this
jay-s: { }
for names {
::n
if first n = "j" { append! jay-s n }
}
; if you can do
filter names { .first = "j" }
Functions return the result or Fail, not nulls.
Unhandled failures become runtime errors (e.g., try or match to recover).
Minimize state; emphasize data transformation pipelines.
Data is immutable by default; mutation is explicit and localized.
"Practically functional": pure functions where possible, controlled side effects where needed.
Declarative validation DSL.
Built-in spreadsheet-like tables for tabular data (e.g., SQL-like ops, columnar transforms).
Avoid paradigm-specific terms (e.g., "methods," "monads").
Focus on "words" (symbols) bound to functions/values in named contexts (scopes).
Failures: Expected, recoverable (e.g., "file not found").
Errors: Bugs; halt execution and debug.
Thanks for exploring Rye! If it sparks your interest, join us—star us on GitHub, follow on Reddit or YouTube, or drop an email. GitHub updates frequently, and we’d love your help with docs, tests, examples, or code! Or just send me an email.