#> Hi! Welcome to the Nulan tutorial. After reading this, you
#> should have a very basic idea of how to write Nulan programs.
#> To start with, all Nulan programs are composed of zero or
#> more expressions. Every expression must have a value. The
#> process of taking an expression and returning a value is
#> called "evaluation".
#> The simplest expressions are literals, which evaluate to
#> themself:
5
#> Try clicking on the above expression `5`. In the lower-right
#> panel, you can see that it evaluates to the number `5`.
#> Another type of literal is a string:
"I'm a string"
#> Strings start with ", contain zero or more characters, and
#> end with ". They're used for storing text.
#> Let's try something more complicated:
5 + 10
#> If you click on the above expression, you'll see it evaluates
#> to the number `15`. That expression is composed of three
#> parts: the number `5`, the symbol `+`, and the number `10`.
#> Now, something I haven't told you yet is that all these
#> examples are fully interactive. To try it out, go ahead and
#> change the expression `5 + 10` to anything you like, and the
#> lower-right panel will automatically update with the answer.
#> Any changes you make are automatically saved, so you can
#> close this tab and reopen it later.
#> If at any time you want to undo the changes you've made,
#> click the "Reset Tutorial" button at the top of the screen.
#> Be careful, it will wipe out your changes, and you can't get
#> them back.
#> Nulan also supports some more math stuff with the usual
#> priority:
1 + 2 * 3 / 4 - 5
#> And you can use parentheses to change the priority of any
#> expression:
(1 + 2) * 3 / (4 - 5)
#> Let's talk about boxes. A box is something that can hold a
#> single value. It can then be unboxed later. To create a box,
#> you use the `box` symbol:
box foo = 2
#> What we've done here is taken the number `2` and placed it
#> inside a box. We then took that box and assigned it to the
#> symbol `foo`. We can then unbox it by simply using the symbol
#> `foo`:
foo
#> You might have noticed that the `box` expression evaluated to
#> `()`. Every expression needs to return a value, but some
#> expressions don't have anything meaningful to return, so they
#> return `()` instead.
#> Because everything in Nulan is an expression, symbols can
#> be used as part of a bigger expression:
foo + 2
#> But boxes aren't just a shorthand for referring to values.
#> Right now, the `foo` box contains the number `2`. But we can
#> put a different number into the box:
foo <= 5
#> Now the `foo` box contains the number `5` rather than the
#> number `2`. It is like as if we unboxed it, took the number
#> out, put a new number in, and then reboxed it again.
#> But even though the stuff inside the box changed, the box
#> itself is the same. This will be important later on.
#> Let's talk about functions. Functions are extremely common in
#> Nulan, so it's important to understand them. A function is a
#> value that can be called with multiple expressions as
#> arguments and then returns an expression.
#> It's okay if you didn't understand anything I just said. I'll
#> take it slow and use lots of examples. First, to create a
#> function, you use the `->` symbol:
-> a a
#> The above expression returns a function which accepts a
#> single argument called `a`. It then returns `a`. Let's try
#> calling the function:
(-> a a) 5
#> One way of thinking about this is to imagine that Nulan
#> replaced the symbol `a` inside the function with the number
#> `5`, then returned `5`.
#> The parentheses are needed because without them, Nulan would
#> treat it as a function that accepts two arguments and always
#> returns `5`.
#> Let's write a function that takes any number and adds `10` to
#> it:
-> a (a + 10)
#> And now let's call it:
(-> a (a + 10)) 5
#> Nulan replaced `a` with `5`, and then returned `(5 + 10)`.
#> This probably doesn't seem very useful. Let's make it more
#> useful by taking the function and placing it into a box:
box add = -> a (a + 10)
#> We created a function using `-> a (a + 10)` and then used
#> `box` to assign it to the `add` symbol.
#> This is common enough that Nulan has a special `def` symbol
#> which is almost exactly like `box`, except that it works
#> better for functions:
def add -> a (a + 10)
#> Now let's call it:
add 5
add 10
add 15
#> Notice that we called it three times, with a different
#> argument each time. In the first call, `a` was replaced with
#> `5`, in the second it was replaced with `10`, and in the
#> third it was replaced with `15`.
#> Let's make a function that accepts three arguments and adds
#> them together:
def add -> a b c
a + b + c
#> Now let's call it:
add 1 2 3
#> Nulan replaced `a` with `1`, `b` with `2`, and `c` with `3`,
#> then returned `1 + 2 + 3`.
#> That was a simple example, but functions can become quite
#> complex. The reason why functions are so useful is that they
#> let you easily evaluate an expression multiple times.
#> Consider these three expressions:
1 + 2 * 3 / 10
1 + 2 * 4 / 10
1 + 2 * 5 / 10
#> The only difference between them is that the first uses `3`,
#> the second uses `4`, and the third uses `5`. Using boxes
#> won't help with the code duplication. Functions to the
#> rescue!
def foo -> a
1 + 2 * a / 10
foo 3
foo 4
foo 5
#> Now the parts that remain unchanged are put into the
#> function. The only part that changes is the argument `a`,
#> which is replaced when the function is called.
#> And so, functions let you take the unchanging parts and put
#> them into a single place, which makes it much much easier as
#> your programs become bigger and more complex.
#> Moving on, let's look at this program:
w/box temp = 5
if temp
temp + 1
w/box temp = 10
if temp
temp + 2
#> The `w/box` symbol is like `box` but the box it creates can
#> only be accessed inside the `w/box` expression.
#> The `if` symbol checks if its first argument is true. If it
#> is, then it will evaluate the second argument. Otherwise
#> it will evaluate the third argument.
#> This pattern of creating a temporary box and using `if` is
#> quite common. Let's try using a function to get rid of the
#> code duplication:
def if-box -> x y
if x
x + y
if-box 5 1
if-box 10 2
#> This works, but it's not very flexible. What if we wanted to
#> subtract the numbers, or divide the numbers, or do something
#> else? The `if-box` function will only add, nothing else.
#> One of the cool things about functions is that because they
#> are values, they can be used as arguments to other functions:
def if-box -> x f
if x
f x
if-box 5 -> x (x + 1)
if-box 10 -> x (x + 2)
#> Here's what happened. We called `if-box` with two arguments:
#> the number `5` and the function `-> x (x + 1)`, which were
#> then replaced like usual.
#> This works, but it's a bit verbose. Let's use a macro to
#> remove the verbosity! You've already seen a bunch of macros,
#> but you didn't realize it. These are all macros:
#> `+` `-` `*` `/` `box` `=` `<=` `->` `def` `w/box` `if`
#> What is a macro? Well, a function has arguments, which are
#> replaced with values, and the function then returns a value.
#> A macro is a special kind of function that has arguments,
#> which are replaced with code, and the macro then returns
#> code.
#> First, to create a macro, we use the `$mac` macro:
$mac if-box ->
#> The above expression took the empty function `->`, converted
#> it into a macro, and then assigned it to the symbol `if-box`.
#> But this macro doesn't do anything yet. Let's start by
#> copying in our original program:
$mac if-box ->
w/box temp = 5
if temp
temp + 1
#> We can't use this macro just yet, though. Remember when I
#> said that macros are given code and return code? Well, right
#> now the macro is returning a value, not code. If we use the
#> `'` macro, we tell Nulan to treat it as code rather than a
#> value:
$mac if-box ->
'w/box temp = 5
if temp
temp + 1
#> Oops, Nulan is saying that the symbol `temp` is undefined.
#> What happened?!
#> Well, do you remember when I said earlier that you can change
#> the value of a box without changing the box itself? The `'`
#> macro returns boxes, not symbols. And the `temp` box isn't
#> defined.
#> To fix this, we'll need to assign `temp` to a box. To create
#> a box, we can use the `w/uniq` macro:
$mac if-box ->
w/uniq temp
'w/box temp = 5
if temp
temp + 1
#> And now it works just fine. But it only works for the first
#> use case, not the second. Let's figure out the parts that
#> change: those parts will need to be arguments to the macro.
#> Well, the expression `5` changes, so let's replace that with
#> an argument:
$mac if-box -> x
w/uniq temp
'w/box temp = x
if temp
temp + 1
#> And the expression `1` changes too, so let's add another
#> argument:
$mac if-box -> x y
w/uniq temp
'w/box temp = x
if temp
temp + y
#> And now we can use it like so:
if-box 5 1
if-box 10 2
#> But wait a minute, this looks just like earlier, when we were
#> using a function. The reason for using a macro is so that we
#> don't hardcode the `+` symbol, so let's start by getting rid
#> of that:
$mac if-box -> x y
w/uniq temp
'w/box temp = x
if temp
y
#> Now, you might be tempted to use it like this:
if-box 5
temp + 1
if-box 10
temp + 2
#> But as you can see, that doesn't work. The reason is because
#> inside the macro, we used `w/uniq` to create a new box, but
#> that box is only accessible *inside* the macro. Instead,
#> let's use a symbol:
$mac if-box -> x y
w/box temp = sym "it"
'w/box temp = x
if temp
y
#> We used the `sym` function to create a new symbol and then
#> inserted that rather than a box. And now we can use the macro
#> like this:
if-box 5
it + 1
if-box 10
it + 2
#> Inside the `if-box` expression, you can use the symbol `it`
#> to refer to the first argument of `if-box`. As you can see,
#> this is indeed shorter than using a function.
#> Because macros always return code, anything you can do with
#> macros, you can do by manually writing the code yourself.
#> The benefit of macros is that you don't *have* to write the
#> code yourself: you can have the macro write the code for you.
#> And so, by using macros wisely, you can write shorter, more
#> readable code.
#> Now, with that out of the way, let's do something fun!
div
style
border-radius "5px"
opacity "1"
width "50px"
height "50px"
background-color "green"
on "click" ->
alert "I'm green!"
div
style
border-radius "5px"
opacity "0.5"
width "50px"
height "50px"
background-color "red"
position "relative"
top "-25px"
left "25px"
on "click" ->
alert "I'm red!"
#> If you look in the upper-right panel, you should see two
#> boxes. If you click on them, they'll tell you what color they
#> are. Both of the boxes were generated with the above code.
#> If you change the code, it'll change the boxes. Try it out!