Rye is a dynamic, homoiconic language inspired by Rebol, Factor, Shell, and Go. Its main claim is that you can have a flexible syntax while being strict about state: more about its principles

If you haven’t already, be sure to first read PART 1 before reading part 2 below.

For intro to language with more explanation visit Meet Rye. If you want to see it use in practice the Cookbook.

This document is about compact code with minimal prose. So let’s go for it…

; # 80% of Rye in 20% of the Time [2/3]

; _inspired by Learn X in Y minutes_

; Heads-up: every Rye token must be separated by a space


; ## The Op-words

; op-words let you call functions with the left value as the first argument

; we call functions by invoking words they are bound to
and true false                              ; returns false
length? { 11 22 33 }                        ; 3
replace "Hello world" "world" "you"         ; "Hello you"

; by adding "." in front we get an op-word.
; op-word calls the same function, but it takes the first argument from the left 
true .and false                             ; false
{ 11 22 33 } .length?                       ; 3
"Hello world" .replace "world" "you"        ; "Hello you"

; operators are op-words even without the dot
18 + 24                                     ; 42
99 > 100                                    ; false

; an op-word takes the first value from the left it ca
10 + 20 .print                              ; prints: 20


; ## The Pipe-words

; pipe-words are similar to op-words but they let expression on the left to evaluate,
; then take the result
10 + 20 |print                              ; prints: 30

; ++ and .print are op-words, |print a pipe-word 
"James " ++ "Bond" .print |print
; prints: 
; Bond                         -- .print takes nearest string "Bond"
; James Bond                   -- |print waits for expression to evaluate

; every function can be called via a word, op-word or pipe-word
; and everything is a function, even: if, for, fn, context, var...


; ## Assignment (binding values to words)

; we use set-words to do this, and they create a constant (immutable word)
name: "Marie"
; name: "Jane"                        -- would produce an error

; we also have a left-set-word
1934 - 1867 :max-age

; mod-words create or change variable words
; they are visually noisy for a reason
surname:: "Skłodowska"
surname:: "Curie"                    ; we could change the surname

; can we change the name with a mod-word?
; name:: "Anne"                      -- still error, name is a constant

; in Rye we always use set-words, so everything is constant, unless
; we have a clear reason not to be


; ## Functions

; `fn` creates a function. It takes a block of arguments and a block of code
add: fn { a b } { a + b }
add 5 10                             ; 15

; the last expression is the result. As we said, first argument is injected
greet: fn { name } { .upper .concat "!" }
greet "darko"                        ; "DARKO!"

; to return and not evaluate a function we use ?get-word
map { "saul" "walter" } ?greet       ; { "SAUL!" "WALTER!" }

; ˙does˙ creates a function with no arguments
beep: does { print "BEEP" }

; `closure` creates a closure, you will see why we use inc! in [3/3]
make-counter: does { cnt:: 0 , closure { } { inc! 'cnt } }

c1: make-counter , c2: make-counter
evals { c1 c1 c1 c2 c2 c1 }          ; { 1 2 3 1 2 4 }


; ## Injected blocks

; functions evaluating blocks of code can inject a left value to the code
with 16 { + 26 |print }                     ; prints: 42

; why would we do this? Because it makes a lot of code flow nicer
{ 1 2 3 4 } .filter { > 2 }                 ; { 3 4 }
map { "anne" "brad" } { .capitalize }       ; { "Anne" "Brad" }
for { 1 2 3 } { * 11 |prns }                ; prints: 11 22 33

name: "Mellissandre"
; `when` injects 'name' into both condition and code blocks
when name { .length? > 10 } { ++ ", impressive name!" }
; prints: Mellissandre, impressive name!

; inside a function, the first argument is also injected
hello: fn { name } { ++ ", hi!" }
hello "Bob"                                 ; "Bob, hi!"


; ## Dialects

; to finish with something lighter. Rye has multiple dialects (sub-interpreters)

; SQL dialect produces prepared statements out of Rye blocks
group: "hobbits"
Open sqlite://lotr.db |Query { select * from crew where group = ?group }
; returns a Table value type (more about it in [3/3])

; Validation dialect validates data
{ "name" "sam" } .dict
|validate>ctx { 
    name: required calc { .capitalize } 
    rings: optional 0 integer 
}
; returns: context { name: "Sam" rings: 0 }

; op-words have no priority. Math dialect follows mathematical priority rules
math/calc { 2 + 2 * ( 12 + 24 / 3 ) }
; returns 42

; other functions use dialects. Like match function, xml and html pestan 

This is the second third of the document. This time, We looked at quite a few Rye’s specifics. Mostly around it’s syntax (or evaluator behavior). Next and the last time few more Rye types, kinds, generic methods, contexts and failures.

Visit the main page or our GitHub.