Update Introduction.

Calvin Rose 2018-09-07 17:53:08 -04:00
parent e8be15c43c
commit 6762f9cf87
2 changed files with 108 additions and 6 deletions

@ -196,9 +196,16 @@ literals without binding them to a symbol.
```
# Evaluates to 40
((fn [x y] (+ x x y)) 10 20)
# Also evaluates to 40
((fn @[x y] (+ x x y)) 10 20)
# Will throw an error about the wrong arity
((fn [x] x) 1 2)
# Will not throw an error about the wrong arity
((fn @[x] x) 1 2)
```
The above expression first creates an anonymous function that adds twice
The first expression creates an anonymous function that adds twice
the first argument to the second, and then calls that function with arguments 10 and 20.
This will return (10 + 10 + 20) = 40.
@ -206,6 +213,9 @@ There is a common macro `defn` that can be used for creating functions and immed
them to a name. `defn` works as expected at both the top level and inside another form. There is also
the corresponding
Note that putting an ampersand in front of the argument list inhibits strict arity checking.
This means that such a function will accept fewer or more arguments than specified.
```lisp
(defn myfun [x y]
(+ x x y))
@ -365,6 +375,8 @@ itself, and the second parameter is the key.
(get "hello, world" 0) # -> 104
```
### Destructuring
In many cases, however, you do not need the `get` function at all. Janet supports destructuring, which
means both the `def` and `var` special forms can extract values from inside structures themselves.
@ -469,20 +481,110 @@ For any given function, use the `doc` macro to view the documentation for it in
To see a list of all global functions in the repl, type the command
```lisp
(getproto *env*)
(table.getproto *env*)
# Or
(all-symbols)
```
Which will print out every built-in global binding
(it will not show your global bindings). To print all
of your global bindings, just use *env*, which is a var
of your global bindings, just use \*env\*, which is a var
that is bound to the current environment.
The convention of surrounding a symbol in stars is taken from lisp
and Clojure, and indicates a global dynamic variable rather than a normal
definition. To get the static environment at the time of compilation, use the
`_env` symbol.
# Prototypes
:)
To support basic generic programming, Janet tables support a prototype
table. A prototype table contains default values for a table if certain keys
are not found in the original table. This allows many similar tables to share
contents without duplicating memory.
```lisp
# One of many Object Oriented schemes that can
# be implented in janet.
(def proto1 @{:type :my.custom1
:behave (fn [self x] (print "behaving " x))})
(def proto2 @{:type :my.custom2
:behave (fn [self x] (print "behaving 2 " x))})
(def thing1 (table.setproto @{} proto1))
(def thing2 (table.setproto @{} proto2))
(print (get thing1 :type)) # prints :my.custom1
(print (get thing2 :type)) # prints :my.custom2
(defn behave [x y]
(let [{:behave f} x]
(f x y)))
(behave thing1 :a) # prints "behaving :a"
(behave thing2 :b) # prints "behaving 2 :b"
```
Looking up in a table with a prototype can be summed up with the following algorithm.
1. `(get my-table my-key)` is called.
2. my-table is checked for the key if my-key. If there is a value for the key, it is returned.
3. if there is a prototype table for my-table, set `my-table = my-table's prototype` and got to 2.
4. Return nil as the key was not found.
Janet will check up to about a 1000 prototypes recursively by default before giving up and returning nil. This
is to prevent an infinite loop. This value can be changed by adjusting the `JANET_RECURSION_GUARD` value
in janet.h.
Note that Janet prototypes are not as expressive as metatables in Lua and many other languages.
This is by design, as adding Lua or Python like capabilities would not be technically difficult.
Users should prefer plain data and functions that operate on them rather than mutable objects
with methods.
# Fibers
:)
Janet has support for single-core asynchronous programming via coroutines, or fibers.
Fibers allow a process to stop and resume execution later, essentially enabling
multiple returns from a function. This allows many patterns such a schedules, generators,
iterators, live debugging, and robust error handling. Janet's error handling is actually built on
top of fibers (when an error is thrown, the parent fiber will handle the error).
A temporary return from a fiber is called a yield, and can be invoked with the `yield` function.
To resume a fiber that has been yielded, use the `resume` function. When resume is called on a fiber,
it will only return when that fiber either returns, yields, throws an error, or otherwise emits
a signal.
Different from traditional coroutines, Janet's fibers implement a signaling mechanism, which
is used to differentiate different kinds of returns. When a fiber yields or throws an error,
control is returned to the calling fiber. The parent fiber must then check what kind of state the
fiber is in to differentiate errors from return values from user defined signals.
To create a fiber, user the `fiber.new` function. The fiber constructor take one or two arguments.
the first, necessary argument is the function that the fiber will execute. This function must accept
an arity of one. The next optional argument is a collection of flags checking what kinds of
signals to trap and return via `resume`. This is useful so
the programmer does not need to handle all different kinds of signals from a fiber. Any untrapped signals
are simply propagated to the next fiber.
```lisp
(def f (fiber.new (fn @[]
(yield 1)
(yield 2)
(yield 3)
(yield 4)
5)))
# Get the status of the fiber (:alive, :dead, :debug, :new, :pending, or :user0-:user9)
(print (fiber.status f)) # -> :new
(print (resume f)) # -> prints 1
(print (resume f)) # -> prints 2
(print (resume f)) # -> prints 3
(print (resume f)) # -> prints 4
(print (fiber.status f)) # -> print :pending
(print (resume f)) # -> prints 5
(print (fiber.status f)) # -> print :dead
(print (resume f)) # -> throws an error because the fiber is dead
```
# Macros

@ -190,7 +190,6 @@ failure to return or error.
| `jmp` | `(jmp label)` | pc = label, pc += offset |
| `jmpif` | `(jmpif cond label)` | if $cond pc = label else pc++ |
| `jmpno` | `(jmpno cond label)` | if $cond pc++ else pc = label |
| `len` | `(len dest ds)` | $dest = length(ds) |
| `ldc` | `(ldc dest index)` | $dest = constants[index] |
| `ldf` | `(ldf dest)` | $dest = false |
| `ldi` | `(ldi dest integer)` | $dest = integer |
@ -198,6 +197,7 @@ failure to return or error.
| `lds` | `(lds dest)` | $dest = current closure (self) |
| `ldt` | `(ldt dest)` | $dest = true |
| `ldu` | `(ldu dest env index)` | $dest = envs[env][index] |
| `len` | `(len dest ds)` | $dest = length(ds) |
| `lt` | `(lt dest lhs rhs)` | $dest = $lhs < $rhs |
| `lti` | `(lti dest lhs rhs)` | $dest = $lhs \<i $rhs |
| `ltim` | `(ltim dest lhs im)` | $dest = $lhs \<i im |