mirror of
https://github.com/janet-lang/janet
synced 2024-12-27 17:00:27 +00:00
Update Introduction.
parent
e8be15c43c
commit
6762f9cf87
112
Introduction.md
112
Introduction.md
@ -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 |
|
||||
|
Loading…
Reference in New Issue
Block a user