Exploring the REPL
Let's start by exploring the language to get a feel for it by opening a REPL (Read-Eval-Print Loop, an interactive mode) and throw some commands in it.
These instructions assume you've already installed dt.
In a shell, just run dt:
$ dt
dt 1.x.x
Remember, I'm pulling for ya. We're all in this together!
»
To exit the REPL, type quit
or use an end-of-file code like Ctrl+d
or
command+d
.
Pro tip: For a better REPL experience, it's also recommended to install rlwrap and create an alias in your shell of choice. Maybe something like
alias dtsh='rlwrap dt'
in.bashrc
or.zsrhc
files, oralias dtsh 'rlwrap dt' && funcsave dtsh
for fish.
From here on these docs will assume you are in a REPL context.
Code following the »
prompt is input, and other lines are output. Copy or
type the code as written here, and feel free to test out other things. It's ok
if you make mistake, just open a new REPL and start fresh.
Say hello! Printing, quotes, and definitions
Ok lightning tour, let's do this!
» "Hello world!" pls
Hello world!
pls
is a command to print the most recent
value to standard output. In the example above we put one value into dt, the
string "Hello world!"
and pls
printed the single value as a single line.
We can also use quotes to define lists of values
and us pls
to print all of those too.
» [ "Hello" "you" "crazy" "world!" ] pls
Hello
you
crazy
world!
Getting back to strings, let's get a little more dynamic. First let's bind a name, feel free to use your own here!
» "Harold" \name:
» name pls
Harold
Now let's define a greet
command. We'll use some printing words we haven't
used before, p
to print a single value as-is, and nl
to add a newline.
» [ "Hello " p name p "!" p nl ] \greet def
» greet
Hello Harold!
We can also use pl
to print a value and a newline at once.
» [ "Hello " p name p "!" p nl ] \greet def
» greet
Hello Harold!
We've used :
to bind a single value to name
, and used def
to bind (and
re-bind!) the execution of a quote of values (including the name
command!) to
greet
. Coming from other languages, :
will fill the role of binding
"variables" and def
will fill the role of defining "functions." In dt terms,
we'll call both of these definitions commands.
Let's redefine name
and greet
again:
» "Bernice" \name:
» greet
Hello Bernice!
Here we can see that the name
we referenced in our greet
definition is a
lazily-evaluated lookup, and the second definition of name
shadows the previous
definition going forward.
Pro tip: You can shadow definitions inside quotes and command definitions, or even reference commands that haven't been defined yet. Use this with extreme prejudice though. It makes the language extremely hackable: you can redefine printing or reading and have a global effect!
Relying on this too much will lead to extreme wackiness and a general inability to understand the program state. This intentionally prevents dt from becoming some big mainstream language with a huge ecosystem of libraries. It's really just for pipes and small scripts.
This redefining behavior departs from similar languages like Forth where you can redefine what 3 means (in dt, you can't) but you can't redefine what a past reference was (in dt, you can).
The re-definition gives a feel of mutation to the definition, but the
underlying value cannot be altered, only re-bound. Let's try it out with
upcase
and downcase
real quick:
» name upcase pls
BERNICE
» name downcase pls
bernice
» name pls
Bernice
Here we learned to print with
p
,nl
,pl
, andpls
. Usepls
for general cases, and the others when you need more control.Commands in a quote (between the
[
and]
characters) have execution deferred.Finally we learned how to define new terms. Congratulations, you have already written dt code!
:
can define a value as-is. (Later we'll see it can define many values.)def
defines the execution of one or more values.
Fibonacci! State, iteration, and conditions
The Fibonacci sequence is a fascinating pattern of numbers that naturally occurs in many places like the growth of pineapples and flowers, or in how light refracts through transparent surfaces.
Let's produce some numbers in the Fibonacci sequence. We'll start with two
integers, and use .s
to check the state of dt.
» 0 1 .s
[ 0 1 ]
.s
can be used to check the state of dt at any time. We can see that the
state of dt
itself is a quote of values. The order is left-to-right just how
we typed it in.
Let's use the state of dt itself to produce a Fibonacci sequence. We'll start by defining a command that can take two numbers from state, produce those two numbers, and also produce their sum.
» .s
[ 0 1 ]
» [[a b]: a b a b + ] \fib def
» fib .s
[ 0 1 1 ]
» fib .s
[ 0 1 1 2 ]
» fib fib fib .s
[ 0 1 1 2 3 5 8 ]
Neat! This is laying out the Fibonacci sequence in dt's main state. Maybe someday we could 3d print a pineapple or something.
Above we used :
to bind two values to the a
and b
. The first time we
used the command, a
bound to 0
, b
bound to 1
. We put them back in the
same order, and then also took a
and b
and added them.
The +
operator is also a command, that takes two values from state and
produces their sum. a b +
in dt is equivalent to a + b
in standard math
notation.
If you want to get nerdy about it (I do, just for a moment) this
a b +
business is sometimes called "Reverse Polish Notation" in mathematics. In linguistics it can be called a "Subject-Object-Verb Grammar" kind of like Japanese or Latin. Bring these up if you want to impress your friends.Anyway, a more practical way to think about it is we're saying "here's
a
and here'sb
" and then giving dt a+
command. In dt there is no look-ahead parsing, or recursive descent, or fancy grammar or anything. Everything is interpreted sequentially left-to-right and top-to-bottom.
Let's make a few more!
» .s
[ 0 1 1 2 3 5 8 ]
» \fib 3 times .s
[ 0 1 1 2 3 5 8 13 21 34 ]
Ahh, good times
! Here we used a \
prefix to create a reference to a command
and then used times
which takes some action, a number, and performs the
action a number of times.
So... now that we've got a bunch of these numbers what should we do with them? For now let's just print them all out. Here are a couple ways we could do it.
-
Using
10 times
since we know how many exist...» \pl 10 times 34 21 13 8 5 3 2 1 1 0
...or,
-
Using a new command,
quote-all
, which will convert the state to a single quote in state.» .s [ 0 1 1 2 3 5 8 13 21 34 ] » quote-all .s [ [ 0 1 1 2 3 5 8 13 21 34 ] ] » pls 0 1 1 2 3 5 8 13 21 34
Note that these both printed all our values, but did so in a different order.
times
executed thepl
command with the most-recent value in state 10 times.pls
(and other commands that operate on quotes) follow a left-to-right ordering.