mirror of
https://github.com/janet-lang/janet
synced 2024-12-26 00:10:27 +00:00
Add documentation on all of the special forms.
This commit is contained in:
parent
dd3fc24a1e
commit
5fa96a6f8c
@ -181,7 +181,7 @@ a function call are evaluated in order, from first argument to last argument).
|
|||||||
Because functions are first-class values like numbers or strings, they can be passed
|
Because functions are first-class values like numbers or strings, they can be passed
|
||||||
as arguments to other functions as well.
|
as arguments to other functions as well.
|
||||||
|
|
||||||
```
|
```lisp
|
||||||
(print triangle-area)
|
(print triangle-area)
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ This prints the location in memory of the function triangle area.
|
|||||||
Functions don't need to have names. The `fn` keyword can be used to introduce function
|
Functions don't need to have names. The `fn` keyword can be used to introduce function
|
||||||
literals without binding them to a symbol.
|
literals without binding them to a symbol.
|
||||||
|
|
||||||
```
|
```lisp
|
||||||
# Evaluates to 40
|
# Evaluates to 40
|
||||||
((fn [x y] (+ x x y)) 10 20)
|
((fn [x y] (+ x x y)) 10 20)
|
||||||
# Also evaluates to 40
|
# Also evaluates to 40
|
||||||
@ -233,7 +233,7 @@ and transform it into some other source code, usually automating some repetitive
|
|||||||
Values can be bound to symbols for later use using the keyword `def`. Using undefined
|
Values can be bound to symbols for later use using the keyword `def`. Using undefined
|
||||||
symbols will raise an error.
|
symbols will raise an error.
|
||||||
|
|
||||||
```
|
```lisp
|
||||||
(def a 100)
|
(def a 100)
|
||||||
(def b (+ 1 a))
|
(def b (+ 1 a))
|
||||||
(def c (+ b b))
|
(def c (+ b b))
|
||||||
@ -245,7 +245,7 @@ cannot be changed after definition. For mutable bindings, like variables in othe
|
|||||||
languages, use the `var` keyword. The assignment special form `set` can then be used to update
|
languages, use the `var` keyword. The assignment special form `set` can then be used to update
|
||||||
a var.
|
a var.
|
||||||
|
|
||||||
```
|
```lisp
|
||||||
(var myvar 1)
|
(var myvar 1)
|
||||||
(print myvar)
|
(print myvar)
|
||||||
(set myvar 10)
|
(set myvar 10)
|
||||||
@ -437,7 +437,7 @@ every iteration of the loop. If it is nil or false, the while loop ends and eval
|
|||||||
the rest of the parameters will be evaluated sequentially and then the program will return to the beginning
|
the rest of the parameters will be evaluated sequentially and then the program will return to the beginning
|
||||||
of the loop.
|
of the loop.
|
||||||
|
|
||||||
```
|
```lisp
|
||||||
# Loop from 100 down to 1 and print each time
|
# Loop from 100 down to 1 and print each time
|
||||||
(var i 100)
|
(var i 100)
|
||||||
(while (pos? i)
|
(while (pos? i)
|
||||||
@ -635,7 +635,7 @@ using janet's builtin quasiquoting facilities.
|
|||||||
(defmacro defn3
|
(defmacro defn3
|
||||||
"Defines a new function."
|
"Defines a new function."
|
||||||
[name args & body]
|
[name args & body]
|
||||||
`(def ,name (fn ,name ,args ,;body)))
|
~(def ,name (fn ,name ,args ,;body)))
|
||||||
```
|
```
|
||||||
|
|
||||||
This is functionally identical to our previous version `defn2`, but written in such
|
This is functionally identical to our previous version `defn2`, but written in such
|
||||||
@ -654,7 +654,7 @@ See what happens if we use a normal unquote for body as well.
|
|||||||
(def args '[x y z])
|
(def args '[x y z])
|
||||||
(defn body '[(print x) (print y) (print z)])
|
(defn body '[(print x) (print y) (print z)])
|
||||||
|
|
||||||
`(def ,name (fn ,name ,args ,body))
|
~(def ,name (fn ,name ,args ,body))
|
||||||
# -> (def myfunction (fn myfunction (x y z) ((print x) (print y) (print z))))
|
# -> (def myfunction (fn myfunction (x y z) ((print x) (print y) (print z))))
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -665,7 +665,7 @@ and a shorthand for it, the ; character.
|
|||||||
When combined with the unquote special, we get the desired output.
|
When combined with the unquote special, we get the desired output.
|
||||||
|
|
||||||
```lisp
|
```lisp
|
||||||
`(def ,name (fn ,name ,args ,;body))
|
~(def ,name (fn ,name ,args ,;body))
|
||||||
# -> (def myfunction (fn myfunction (x y z) (print x) (print y) (print z)))
|
# -> (def myfunction (fn myfunction (x y z) (print x) (print y) (print z)))
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -679,7 +679,7 @@ the following macro
|
|||||||
(defmacro max1
|
(defmacro max1
|
||||||
"Get the max of two values."
|
"Get the max of two values."
|
||||||
[x y]
|
[x y]
|
||||||
`(if (> ,x ,y) ,x ,y))
|
~(if (> ,x ,y) ,x ,y))
|
||||||
```
|
```
|
||||||
|
|
||||||
This almost works, but will evaluate both x and y twice. This is because both show up
|
This almost works, but will evaluate both x and y twice. This is because both show up
|
||||||
@ -692,7 +692,7 @@ We can do better:
|
|||||||
(defmacro max2
|
(defmacro max2
|
||||||
"Get the max of two values."
|
"Get the max of two values."
|
||||||
[x y]
|
[x y]
|
||||||
`(let [x ,x
|
~(let [x ,x
|
||||||
y ,y]
|
y ,y]
|
||||||
(if (> x y) x y)))
|
(if (> x y) x y)))
|
||||||
```
|
```
|
||||||
@ -729,7 +729,7 @@ our macro once more for a fully correct macro.
|
|||||||
[x y]
|
[x y]
|
||||||
(def $x (gensym))
|
(def $x (gensym))
|
||||||
(def $y (gensym))
|
(def $y (gensym))
|
||||||
`(let [,$x ,x
|
~(let [,$x ,x
|
||||||
,$y ,y]
|
,$y ,y]
|
||||||
(if (> ,$x ,$y) ,$x ,$y)))
|
(if (> ,$x ,$y) ,$x ,$y)))
|
||||||
```
|
```
|
||||||
|
206
doc/Specials.md
Normal file
206
doc/Specials.md
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
# Special Forms
|
||||||
|
|
||||||
|
Janet is a lisp and so is defined in terms of mostly S-expressions, or
|
||||||
|
in terms of Janet, tuples. Tuples are used to represent function calls, macros,
|
||||||
|
and special forms. Most functionality is exposed through functions, some
|
||||||
|
through macros, and a minimal amount through special forms. Special forms
|
||||||
|
are neither functions nor macros -- they are used by the compiler to directly
|
||||||
|
express a low level construct that can not be expressed through macros or functions.
|
||||||
|
Special forms can be thought of as forming the real 'core' language of janet.
|
||||||
|
|
||||||
|
Below is a reference for all of the special forms in Janet.
|
||||||
|
|
||||||
|
## (def name meta... value)
|
||||||
|
|
||||||
|
This special form binds a value to a symbol. The symbol can the be substituted
|
||||||
|
for the value in subsequent expression for the same result. A binding made by def
|
||||||
|
is a constant and cannot be updated. A symbol can be redefined to a new value, but previous
|
||||||
|
uses of the binding will refer to the previous value of the binding.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(def anumber (+ 1 2 3 4 5))
|
||||||
|
|
||||||
|
(print anumber) # prints 15
|
||||||
|
```
|
||||||
|
|
||||||
|
Def can also take a tuple, array, table or struct to perform destructuring
|
||||||
|
on the value. This allows us to do multiple assignments in one def.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(def [a b c] (range 10))
|
||||||
|
(print a " " b " " c) # prints 0 1 2
|
||||||
|
|
||||||
|
(def {:x x} @{:x (+ 1 2)})
|
||||||
|
(print x) # prints 3
|
||||||
|
|
||||||
|
(def [y {:x x}] @[:hi @{:x (+ 1 2)}])
|
||||||
|
(print y x) # prints hi3
|
||||||
|
```
|
||||||
|
|
||||||
|
Def can also append metadata and a docstring to the symbol when in the global scope.
|
||||||
|
If not in the global scope, the extra metadata will be ignored.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(def mydef :private 3) # Adds the :private key to the metadata table.
|
||||||
|
(def mydef2 :private "A docstring" 4) # Add a docstring
|
||||||
|
|
||||||
|
# The metadata will be ignored here because mydef is
|
||||||
|
# accessible outside of the do form.
|
||||||
|
(do
|
||||||
|
(def mydef :private 3)
|
||||||
|
(+ mydef 1))
|
||||||
|
```
|
||||||
|
|
||||||
|
## (var name meta... value)
|
||||||
|
|
||||||
|
Similar to def, but bindings set in this manner can be updated using set. In all other respects is the
|
||||||
|
same as def.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(var a 1)
|
||||||
|
(defn printa [] (print a))
|
||||||
|
|
||||||
|
(printa) # prints 1
|
||||||
|
(++ a)
|
||||||
|
(printa) # prints 2
|
||||||
|
(set a :hi)
|
||||||
|
(printa) # prints hi
|
||||||
|
```
|
||||||
|
|
||||||
|
## (fn name? args body...)
|
||||||
|
|
||||||
|
Compile a function literal (closure). A function literal consists of an optional name, an
|
||||||
|
argument list, and a function body. The optional name is allowed so that functions can
|
||||||
|
more easily be recursive. The argument list is a tuple of named parameters, and the body
|
||||||
|
is 0 or more forms. The function will evaluate to the last form in the body. The other forms
|
||||||
|
will only be evaluated for side effects.
|
||||||
|
|
||||||
|
Functions also introduced a new lexical scope, meaning the defs and vars inside a function
|
||||||
|
body will not escape outside the body.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(fn []) # The simplest function literal. Takes no arguments and returns nil.
|
||||||
|
(fn [x] x) # The identity function
|
||||||
|
(fn identity [x] x) # The identity function - the name will also make stacktraces nicer.
|
||||||
|
(fn [] 1 2 3 4 5) # A function that returns 5
|
||||||
|
(fn [x y] (+ x y)) # A function that adds its two arguments.
|
||||||
|
|
||||||
|
(fn [& args] (length args)) # A variadic function that counts its arguments.
|
||||||
|
|
||||||
|
# A function that doesn't strictly check the number of arguments.
|
||||||
|
# Extra arguments are ignored, and arguments not passed are nil.
|
||||||
|
(fn [w x y z &] (tuple w w x x y y z z))
|
||||||
|
```
|
||||||
|
|
||||||
|
## (do body...)
|
||||||
|
|
||||||
|
Execute a series of forms for side effects and evaluates to the final form. Also
|
||||||
|
introduces a new lexical scope without creating or calling a function.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(do 1 2 3 4) # Evaluates to 4
|
||||||
|
|
||||||
|
# Prints 1, 2 and 3, then evaluates to (print 3), which is nil
|
||||||
|
(do (print 1) (print 2) (print 3))
|
||||||
|
|
||||||
|
# Prints 1
|
||||||
|
(do
|
||||||
|
(def a 1)
|
||||||
|
(print a))
|
||||||
|
|
||||||
|
# a is not defined here, so fails
|
||||||
|
a
|
||||||
|
```
|
||||||
|
|
||||||
|
## (quote x)
|
||||||
|
|
||||||
|
Evaluates to the literal value of the first argument. The argument is not compiled
|
||||||
|
and is simply used as a constant value in the compiled code. Preceding a form with a
|
||||||
|
single quote is shorthand for `(quote expression)`.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(quote 1) # evaluates to 1
|
||||||
|
(quote hi) # evaluates to the symbol hi
|
||||||
|
(quote quote) # evaluates to the symbol quote
|
||||||
|
|
||||||
|
`(1 2 3) # Evaluates to a tuple (1 2 3)
|
||||||
|
`(print 1 2 3) # Evaluates to a tuple (print 1 2 3)
|
||||||
|
```
|
||||||
|
|
||||||
|
## (if condition when-true when-false?)
|
||||||
|
|
||||||
|
Introduce a branching construct. The first form is the condition, the second
|
||||||
|
form is the form to evaluate when the condition is true, and the optional
|
||||||
|
third form is the form to evaluate when the condition is false. If no third
|
||||||
|
form is provided it defaults to nil.
|
||||||
|
|
||||||
|
The if special form will not evaluate the when-true or when-false forms unless
|
||||||
|
it needs to - it is a lazy form, which is why it cannot be a function or macro.
|
||||||
|
|
||||||
|
The condition is considered false only if it evaluates to nil or false - all other values
|
||||||
|
are considered true.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(if true 10) # evaluates to 10
|
||||||
|
(if false 10) # evaluates to nil
|
||||||
|
(if true (print 1) (print 2)) # prints 1 but not 2
|
||||||
|
```
|
||||||
|
|
||||||
|
## (splice x)
|
||||||
|
|
||||||
|
The splice special form is an interesting form that doesn't have an analog in most lisps.
|
||||||
|
It only has an effect in two places - as an argument in a function call, or as the argument
|
||||||
|
to the unquote form. Outside of these two settings, the splice special form simply evaluates
|
||||||
|
directly to it's argument x. The shorthand for splice is prefixing a form with a semicolon.
|
||||||
|
|
||||||
|
In the context of a function call, splice will insert *the contents* of x in the parameter list.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(+ 1 2 3) # evaluates to 6
|
||||||
|
|
||||||
|
(+ @[1 2 3]) # bad
|
||||||
|
|
||||||
|
(+ (splice @[1 2 3])) # also evaluates to 6
|
||||||
|
|
||||||
|
(+ ;@[1 2 3]) # Same as above
|
||||||
|
|
||||||
|
(+ ;(range 100)) # Sum the first 100 natural numbers
|
||||||
|
|
||||||
|
(+ ;(range 100) 1000) # Sum the first 100 natural numbers and 1000
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice that this means we rarely will need the `apply` function, as the splice operator is more flexible.
|
||||||
|
|
||||||
|
The splice operator can also be used inside an unquote form, where it will behave like
|
||||||
|
an `unquote-splicing` special in other lisps.
|
||||||
|
|
||||||
|
## (while condition body...)
|
||||||
|
|
||||||
|
The while special form compiles to a C-like while loop. The body of the form will be continuously evaluated
|
||||||
|
until the condition is false or nil. Therefor, it is expected that the body will contain some side effects
|
||||||
|
of the loop will go on for ever. The while loop always evaluates to nil.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(var i 0)
|
||||||
|
(while (< i 10)
|
||||||
|
(print i)
|
||||||
|
(++ i))
|
||||||
|
```
|
||||||
|
|
||||||
|
## (set l-value r-value)
|
||||||
|
|
||||||
|
Update the value of a var l-value to a new value r-value. The set special form will then evaluate to r-value.
|
||||||
|
|
||||||
|
The r-value can be any expression, and the l-value should be a bound var.
|
||||||
|
|
||||||
|
## (quasiquote x)
|
||||||
|
|
||||||
|
Similar to `(quote x)`, but allows for unquoting within x. This makes quasiquote useful for
|
||||||
|
writing macros, as a macro definition often generates a lot of templated code with a
|
||||||
|
few custom values. The shorthand for quasiquote is a leading tilda `~` before a form. With
|
||||||
|
that form, `(unquote x)` will evaluate and insert x into the unquote form. The shorthand for
|
||||||
|
`(unquote x)` is `,x`.
|
||||||
|
|
||||||
|
## (unquote x)
|
||||||
|
|
||||||
|
Unquote a form within a quasiquote. Outside of a quasiquote, unquote is invalid.
|
Loading…
Reference in New Issue
Block a user