This is a brief getting-started guide to rhoScript which
introduces the key ideas behind the design of rhoScript.
While rhoScript defines both the high-level and the low-level dense code, all
code here is in the rhoScript high-level language. The only difference
is that instead of having to manually compute function lengths, and type
call functions by typing bit by bit, coding is done with
multi-character mnemonics. Think of low-level rhoScript as machine
code, and the high-level rhoScript as an assembly language, with an
assembler which converts one to the other.
It is important to realize this means that while the functions here
look to be quite long, in reality they are significantly shorter.
The online repl will report the actual size of entered commands. Click
on the run
buttons to send any of the
commands to the online repl.
This getting started tutorial will cover the basics needed to start writing
rhoScript in a few minutes. This is not an attempt to provide an exaustive
look at how rhoScript works, or a description of all the commands. See the
for the former and the
for the latter.
Basic Commands and Stack Operations
The main mode of operation in rhoScript is the stack. Anything that
is not a builtin function pushes itself on to the stack.
For example, the following program will add two integers.
First 1 is pushed on the stack, then 2 is pushed on the stack.
The add function pops the top two values off of the stack, adds them,
and then pushes the result back on to the stack. The result is 3. The
print function then prints the result. The final print is not actually
required: rhoScript automatically prints out the top element of the
stack at the end of the program.
You can create a function by enclosing commands with parentheses. This
pushes the function on top of the stack. Other operations can then
use this function from the top of the stack.
This program is very similar to the first program, but with on minor difference.
Instead of pushing 1, 2, adding, and printing, the first thing that is done is
a function is pushed on to the stack that, when called, will print 3. Then, the
builtin call is executed which actually runs the top element of the stack.
In the previous case, there is no reason to push a function. However consider the
following case where it is required.
10 range (2 multiply) map run
In this case, first the integer 10 is pushed. Then range is called, which
constructs the list of integers from 0 (inclusive) to 10 (exclusive). Next,
a function is pused to the stack which, when executed, will multiply
the top element of the stack by two. Finally, map is called. Map is one of
many higher-order functions built in to rhoScript. Map takes two arguments:
a list, and a function. It then constructs a new list by calling the function
on each element of the list. The result, in this case, will be the even integers
starting from 0 and ending at 18. Note that map is not a destructive map. rhoScript
has every other higher order function you may be familiar with: including reduce,
filter, fold, but also some you may not be familiar with: unreduce, tabulate, and
fixpoint are just a few of them.
List operations in rhoScript are usually lazy operations. That is, they
don't run over every element all at once. They are only executed when
they are required. This means we can construct and manipulate infinite
naturals rest prefixes (sum) map 100 take run
We begin by constructing an infinite list of all of the integers from 1. Then,
we gather all of its prefixes. This is a list, where the first element is the
empty list, the second is the list containing just 1, the third contains
1 and 2, and so on. Mapping the sum over the list gives the triangle number
We then take 100 of them.
As a more complicated example, we show how to construct all subsequences of a
naturals prefixes (suffixes) map flatten uniq 100 take run
First we create an infinite stream of integers. We then take all prefixes of this
list. For each prefix, we then
take all suffixes. This leaves us with several duplicates, so we only take the
unique ones. Finally, we remove the first 100 and display them.
Arguments and Function Modifiers
The functions in rhoScript are the primary difference between it and most other
stack based languages. Below we describe two of the important features of
The first of these are function arguments, which allow partial bypassing of the
stack for the top four values on a stack when a function is called.
One of the downside of stack-based languages is that they can sometimes require
a relatively large amount of stack manipulation to preform a few meaningful operations.
To make this less common, rhoScript automatically binds four variables whenever a
function is called, from the top four elements on the stack.
100 range (2 mod 0 eq arg-a 3 mod 0 eq or) filter run
This function returns those integers which are divisible by either 2 or 3. It saves
saves calling both duplicate/swap within the function by using the saved arguments.
arg-a refers to the top element of the stack when the function was called. The
same is true for arguments b through d.
Next, we describe function modifiers. These modifiers state common properties
about functions which change their behavior. There are two main function modifiers,
one states whether or not a function is "restoring" and the other if a function
Functions are stack-restoring by default, but can be made to be not. This allows
the stack to be modified in between function calls of higher-order functions.
1 10 range (*non-restoring add dup) map run
Under normal situations, the top element of the stack would be a list from 1 to 10
(inclusive). The extra argument on the stack at the end of each map call is discarded.
The non-restoring stack, means that while the first time the
mapping function is called, there is already a 1 on the stack, the second time,
there is already a 2 on the stack. This results in the list (1 2 4 7 11 16 22
29 37 46). Functions that are not stack restoring are automatically forced; this means
you can not use non-stack-restoring functions with infinite lists.
In rhoScript, strings are simply lists of integers, each being a single byte.
A string is created on the stack by surrounding text with double quotes.
For example, the following program will concatenate two strings.
"hello " "world" concatenate run
At the moment, strings
take a full eight bits per character, and they are not compressed in any way. In the
future, strings will be compressed in multiple different ways, with the best method
selected as the low-level representation.
Unlike most languages, strings need not be one dimensional lists. rhoScript also
allows strings to be a list of list of bytes.
In this case, the string is treated as a if each sub-list was a line, and they are
joined with a newline when operated on.
"four word list here" split-by-spaces list-to-string transpose run
First, a string represented as a one dimensional byte array is created, which contains
the byte 0x10 in three places. split-by-newlines results in a string with four elements:
"four", "word", "list", and "here". Converting it to a string results in a new
(two-dimensional) string, with the same four substrings. Finally, "transpose" operates
by exchanging rows and columns. The resulting string will have the same message,
with text printed in columns.
You should now know enough to be able to write some simple rhoScript programs.
Continue from here by either writing some programs
or reading the specification