# *doc-width* is bound to the keyword :doc-width - it can be used in place of
# :doc-width in a call to `dyn`, `setdyn`, or `with-dyns` and is preferable
# to using the keyword directly. When set to a number, it indicates the
# maximum width (in columns) of a row of text returned by `doc-format`.
# - Like *doc-color*, *doc-width* can be used to adjust the output of
# `doc-format`.
# - When the :doc-width dynamic binding is not set, the default width is 80.
# - By default, `doc-format` adds 4 space indentation and subtracts 8 from
# the value of the :doc-width dynamic binding to calculate a max width.
# Default:
# repl> (doc doc)
#
#
# macro
# boot.janet on line 3573, column 1
#
# (doc &opt sym)
#
# Shows documentation for the given symbol, or can show a list of
# available bindings. If sym is a symbol, will look for documentation
# for that symbol. If sym is a string or is not provided, will show
# all lexical and dynamic bindings in the current environment
# containing that string (all bindings will be shown if no string is
# given).
# With *doc-width*:
# repl> (with-dyns [*doc-width* 40] (doc doc))
#
#
# macro
# boot.janet on line 3573, column 1
#
# (doc &opt sym)
#
# Shows documentation for the
# given symbol, or can show a
# list of available bindings.
# If sym is a symbol, will
# look for documentation for
# that symbol. If sym is a
# string or is not provided,
# will show all lexical and
# dynamic bindings in the
# current environment
# containing that string (all
# bindings will be shown if
# no string is given).
*doc-width*quexxonPlayground# When the :doc-color dynamic binding referenced by *doc-color* is truthy,
# the doc-format function replaces a minimal subset of Markdown markup with
# the corresponding ANSI escape codes.
#
# The following markup is supported:
# - *this will be underlined*
# - **this will be bold**
# - `(+ 1 2 3)` <- backticks for code
#
# You may be surprised by *underline* since the same markup is used to
# indicate italics in Markdown. This is likely a tradeoff for compatibility;
# historically, the italic attribute has not been widely supported by
# terminal emulators.
#
# The best way to see the effect of *doc-color* is try the following examples
# in the Janet REPL.
# By default, *doc-color* is enabled.
(print (doc-format "*underline*. **bold**. `(code)`."))
# Set the dynamic binding to a falsy value to disable doc-format's ANSI
# escape code substition.
(with-dyns [*doc-color* false]
(print (doc-format "*underline*. **bold**. `(code)`.")))
# N.B.: At the time of writing, no docstrings in the core API take advantage of
# the bold or underline markup As a result, you may not see any difference in
# the doc formatting if your terminal theme uses the same hue for white and
# bright white (a few terminals that I tested on Linux make no distinction
# between the two colors in their default configuration).
*doc-color*quexxonPlayground# In Linux, "everything is a file" so:
(def pid (os/getpid))
pid
# =>
367537
(def statm-path (string "/proc/" pid "/statm"))
statm-path
# =>
"/proc/367537/statm"
(slurp statm-path)
# =>
@"1380 971 683 82 0 362 0\n"
slurpsogaiuPlayground# in the file ex.janet
(main [_ & args]
(print "write something and I'll echo it")
(def x (getline))
(print x))
# in the terminal:
# $ janet ex.janet
# write something and I'll echo it
# bla <- you wrote this
# bla
getlineveqqqPlayground# sh/$ can't expand "~" so you must build it:
# (def key "D8E4DB18BF87FLEW7402BBE3AA91B16F4A65C4C9") # use your gpg key ID
(defn copy-and-encrypt-password-store [key-id]
(with [out (file/open "pass-backup.tar.gz.gpg" :w)]
(sh/$ tar -czf ,(string (os/getenv "HOME") "/.password-store") | gpg --encrypt --recipient ,key-id ,out)))
# tar -cz ~/.password-store/ | gpg --encrypt --recipient YOUR_KEY_ID > pass-backup.tar.gz.gpg
sh/$veqqqPlayground
# sh/$'s contents are quasiquoted, allowing direct or string arguments
# so you need to unquote , variables:
(def out (file/open "trust-db.txt" :w))
(sh/$ "gpg" "--export-ownertrust" ,out) # > requires an opened file object
(file/close out)
# note how > requires an opened file object
(with [out (file/open "trust-db.txt" :w)]
(sh/$ gpg --export-ownertrust ,out))
sh/$veqqqPlayground# sorted allocates the full memory of its input
(def d2 (range 10000000 0 -1)) # 87mb
(sorted d2) # now 167 mb
sortedveqqqPlayground# Upgrade mutates, but I wasn't sure whether it'd matter using the pure sorted or mutative sort. So I
# did a simple test.
(import spork)
# create data before test
(def d @{:data (range 10000000 0 -1)})
(spork/test/timeit (update d :data sort)) # 4.32930135726929 seconds and 87.4 MB
(def d @{:data (range 10000000 0 -1)}) # 87.4 MB
(spork/test/timeit (update d :data sorted)) # 4.49482655525208 seconds and 167 MB
# Where did those memory numbers come from? With only the data, the Janet process
# uses 87.4 MB. Timewise, they take the same amount of time but on starting, sorted
# prepares memory equivalent to its input. To check ram within Janet:
(def pid (os/getpid)) # => "/proc/367537/statm"
(def statm-path (string "/proc/" pid "/statm")) # => 367537
(slurp statm-path) # => @"40444 40038 710 82 0 39426 0\n"
# Collecting garbage:
(gccollect)
(slurp statm-path) # => @"20912 20503 695 82 0 19894 0\n"
spork/test/timeitveqqqPlayground# Consider this problem: As a module author, I want to define a dynamic
# binding `:debug` to provide additional diagnostic information when an
# error occurs in a module function. I'll use `defdyn` to create a public
# alias for my dynamic binding:
# In the file: my-mod.janet
# The `defdyn` macro creates the dynamic binding :debug, and explicitly
# exposes it in the module's API as `my-mod/*debug*`.
(defdyn *debug*)
# contrived function - the important thing is that it enables additional
# diagnostic logging when the `*debug*` alias's reference dynamic binding
# is true.
(defn my-func [& params]
(try
(do
(when (empty? params)
(error "missing required argument")))
([err fib]
(when (dyn *debug*)
(print "[diagnostics] ..."))
(error (string "error occurred: " err)))))
# Let's test our module from the REPL.
# $ repl:1:> (import /my-mod)
# $ repl:2:> (setdyn my-mod/*debug* true) # enable debug mode
# $ repl:3:> (my-mod/my-func)
# [diagnostics] ...
# error: error occured: missing required argument
# in my-func [my-mod.janet] on line 11, column 7
# in thunk [repl] (tail call) on line 4, column 1
# entering debug[1] - (quit) to exit
# debug[1]:1:>
# We see our diagnostic message, but we also entered Janet's built-in
# debugger! That's not what we intended. Why did this happen?
# It turns out that our module's `my-mod/*debug*` alias is namespaced, but the
# referenced `:debug` dynamic binding is not. We've created a collision with
# the `:debug` dynamic binding in the core API that activates the debugger
# on error.
# $ repl> *debug*
:debug
# $ repl> my-mod/*debug*
:debug
# The solution is to set the :defdyn-prefix dynamic binding to add a namespace
# for all dynamic bindings in our module. We should use a reasonably unique
# prefix - let's go with the name of the module's public git repo. It's okay
# for the prefix to be long - it will be used as the key for the dynamic
# binding in the current environment, but the user won't typically interact
# with it directly. The prefix is effectively "hidden" from the user. We'll
# prepend this line at the top of our module:
(setdyn *defdyn-prefix* "codeberg.org/quexxon/my-mod/")
# Now our `my-mod/*debug*` alias refers to an isolated, namespaced binding:
# $ repl:1:> (import /my-mod)
# $ repl:2:> my-mod/*debug*
:codeberg.org/quexxon/my-mod/debug
# $ repl:3:> (setdyn my-mod/*debug* true) # enable debug mode
# $ repl:4:> (my-mod/my-func)
# [diagnostics] ...
# error: error occured: missing required argument
# in my-func [my-mod.janet] on line 11, column 7
# in thunk [repl] (tail call) on line 4, column 1
# $ repl:5:>
# Much better! We see our diagnostic message, but we didn't trigger Janet's
# debugger. As a general rule, always set `*defdyn-prefix*` to a unique
# value when defining dynamic bindings for your module.
*defdyn-prefix*quexxonPlayground# Note that the earlier sort func is executed later
(update @{:data @[:cherry :orange]} :data (comp sort |(array/push $ :apple)))
# => @{:data @[:apple :cherry :orange]}
compveqqqPlayground(update @{:data @[:cherry :orange]} :data (comp sorted |(array/push $ :apple)))
# => {:data @[:apple :cherry :orange]}
# note that all data structures must be mutable with @
(def c @{:size :small :roast :dark :add-ins @[:soy :almond :soy]})
(defn add! [coffee extra]
(update coffee :extras (comp sort |(array/push $ extra))))
(add! c :chocolate)
updateveqqqPlayground# From the REPL, always nil
repl> (dyn *current-file*)
nil
# As an expression to the CLI, always nil
$ janet -e '(pp (dyn *current-file*))'
nil
# Given the file /tmp/janet/test.janet with the following contents:
(pp (dyn *current-file*))
# Note how the value reflects the given file path
~ $ janet /tmp/janet/test.janet
"/tmp/janet/test.janet"
/tmp/janet $ janet ./test.janet
"./test.janet"
/tmp/janet $ janet test.janet
"test.janet"
/tmp/janet $ janet ../janet/test.janet
"../janet/test.janet"
# The following are equivalent - the first is preferred.
# The key insight is that dynamic bindings are just keyword/value
# pairs in the current fiber's environment table.
(dyn *current-file*)
(dyn :current-file)
((curenv) *current-file*)
((curenv) :current-file)
*current-file*quexxonPlayground(var x 1)
(loop [n :range-to [1 5]]
( x n))
x # => 120
*=quexxonPlayground# To make "stub functions" in an image, solving c interop problems like this:
# I hoped that I would be able to unmarshal the runtime.jimage, load my C functions,
# and Janet would use my app's functions instead of the stubs. However, during
# compilation, Janet seems to precompile all the top-level function calls. This
# means hotswapping the functions does nothing, and the Janet code continues to
# use the stubs instead.
#
# I was hoping that there would be some way of telling Janet "don't precompile
# this please", either a (defstubn ...) or the ability to do
# (defn internal/redraw-ui [opts] (dyn)), where (dyn) is some abstract type that
# Janet can't precompute directly.
# https://github.com/janet-lang/janet/issues/1642
# pretend this is the stub function we want to replace
(defn greet [] (print "hello world"))
# make-image-dict determines which values will be filled in at
# "deserialization" time
(put make-image-dict greet 'greet)
(def pretend-env @{
'some-function (fn [] (greet) (greet))})
(def compiled (make-image pretend-env))
# notice: the string "hello world" does not appear in the compiled result
(pp compiled)
# if we just try to (load-image compiled) at this point, it will raise,
# because we haven't specified what to do about 'greet
# we have to make an entry in the load-image-dict. note that
# it does not have to be the same value!
(put load-image-dict 'greet (fn [] (print "something else!")))
# now we can call load-image
(def pretend-env-reparsed (load-image compiled))
# and when we run this, we will see the replacement that we gave
((pretend-env-reparsed 'some-function))
make-image-dictveqqqPlayground(import joy)
# given a handler
(defn handle-func [request]
(joy/text/plain (string "Hi " ((request :params) :name))))
# joy/routes will make handlers
(def r (joy/routes [:get "/hi/:name" :handle-func]))
# which you can pass to joy/app
(joy/server (joy/app {:routes r}) 9002)
# visit at localhost:9002/hi/bob
joy/routesveqqqPlayground