Sogaiu has amazing material strown about his github account.
let
Overall, people seem to prefer def for everything, though experienced lispers will opt for let. Personally, I prefer let for 3+ declarations at the same time, but def for 1. With 2, they're equivalent (in chars etc.) Occasionally I like the style of doing all login within the let declarations themselves:
(let [a 1
b 2
c (+ a b )
d (+ c 1 )
e (* 2 d )]
e )
In reality, most of those steps would be applying some complex function (predefined or e.g. a network call); often they could be replaced with -> or a transducer (I have a WiP library for them.)
Here's a real example:
(defn github-auth [request ]
(let [code (get-in request [:query-string :code ])
result (http/post "https://github.com/login/oauth/access_token"
(string/format "client_id=%s&client_secret=%s&code=%s"
(env :github-client-id )
(env :github-client-secret )
code )
:headers {"Accept" "application/json" })
result (json/decode (result :body ) true true )
access-token (get result :access_token )
auth-response (http/get "https://api.github.com/user"
:headers {"Authorization" (string "token " access-token )})
auth-result (json/decode (auth-response :body ) true true )]
(var account (db/find-by :account :where {:login (auth-result :login )}))
(unless account
(set account (db/insert :account {:login (auth-result :login )
:access-token access-token })))
(db/update :account (account :id ) {:access-token access-token })
(-> (redirect-to :home/index )
(put-in [:session :login ] (account :login )))))
To mark deprication, add :depricated to def, var, defn or defmacro (after docstring, before param list). There are 3 lint levels which triger compiler warnings based on *lint-levels* and *lint-warn*:
:strict - only issues for nit-picky things:nomal - defaultrelaxed - display warning even with relaxed settingFor example, using the variable (def example :deprecated "example") you'll see: repl:2:1: compile warning (normal): normal-binding is deprecated. To set the levels: (def example {:depricated :relaxed} "example") You can set special warnings via the linting array (dyn :macro-lints).
:private
https://janet-lang.org/docs/functions.html#Optional-Flags
Each fiber is a complete call stack which can be suspended and resumed, like reified continuations (first-class objects you can pass around) wherefiber/new captures a continuation you can suspend or resume:
(def fiber-func (yield 1 ) (yield 2 )) # must accept arity 0
(def f (fiber/new fiber-func ))
(fiber/status f ) #=> :new
(resume f ) # => 1
(fiber/status f ) #=> :pending
(resume f ) # => 2
(resume f ) # => nil
(fiber/status f ) #=> :dead
Fibers handle errors. On errors, control returns to the parent fiber. Pass fiber/new the :e flag like (def f (fiber/new block :e)) to trap an error (other signals propagate normally).
(defn err-func [] (error "oops" ))
(def f (fiber/new err-func :e ))
(resume f ) #=> "oops"
(fiber/status f ) #=> :error
(fiber/last-value f ) #=> "oops"
Handle these errors with try or protect:
(if (= (fiber/status f ) :error )
(print "caught error: " res )
(print "value returned: " res ))
# `try` executes the body, in case of error it binds the error
# and raising fiber in the 2nd clause
(try # execute first block, on error, execute 2nd block
(error "oops" )
([err fib ] # bind err and fiber here
(print "caught error: " err )
(fiber/status fib )))
(protect # returns [true return-val] or [false err-msg]
(if (error "oops" )
"All good!"
"An error!" ))
Dynamic variables are scoped to fibers:
(var *my-var* :default )
(defn show-var [] (print (dyn *my-var* )))
(def f (fiber/new (fn [] (setdyn *my-var* :fiber-local ) (show-var ))))
(resume f ) #=> :fiber-local
*my-var* #=> :default
Also read Sogaiu's notes. Pepe offered a simple mental model:
def puts a binding in the environment table which (curenv) shows. But we can use e.g. print without importing anything, how?
Janet looks for keys in a table, then its parent (prototype) etc. There is a "shadow" environment prototype, holding the core language. (all-bindings) will show both. To see the root environment: (pp (table/getproto (curenv)))
(table/setproto (curenv) @{}) will break a Janet process by removing the root-env so you can't use print or anything else!
Ian Henry gooes into its relationship with images.
### in a repl
(def my-module @{:public true })
# (var voice [:unvoiced :voiced]) # do stuff here...
(spit "test.jimage" (make-image (curenv )))
You can then run that image with janet -i test.jimage, but to resume it:
(defn restore-image [image ]
(loop [[k v ] :pairs image ]
(put (curenv ) k v )))
(restore-image (load-image (slurp "test.jimage" )))