Welcome, I'm happy to see you here! Feel free to pick a function and add a happy example, the more the merrier!
# Some tips for working with bytes and unicode:
(string/bytes "что-нибудь" ) #> (209 135 209 130 208 190 45 208 189 208 184 ...
(print (string/from-bytes 208 176 208 177 )) #> аб
(map string/from-bytes (string/bytes "что-нибудь" )) #> @["\xD1" "\x87" "\xD1" "\x82" "\xD0" "\xBE" ...
# Print renders "\xD1" "\x87" as ч, as unicode characters may have multiple bytes
# So use apply:
(apply print (map string/from-bytes
(string/bytes "что-нибудь" ))) #> что-нибудь # many terminals will truncate long data, use this if you need to see/copypaste it:
(defn full-print `Print a large DS without truncation` [ds ] (print (string/format "%m" ds )))(defn bench `Feed bench a wrapped func and int, receive int for time in ns`
[thunk times ]
(def start (os/clock :cputime :tuple ))
(loop [_ :range [times ]]
(thunk ))
(def end (os/clock :cputime :tuple ))
(/ (+ (* (- (end 0 ) (start 0 )) 1e9 )
(- (end 1 ) (start 1 )))
times ))
# it turns out os/clock is pretty darn fast (comparatively)
(def iterations 2000000 )
(bench |(os/clock :cputime :tuple ) iterations ) # 1283.30053 ns
(bench |(slurp "/proc/self/schedstat" ) iterations ) # 7881.451760 ns
# these basically benchmark slurp
(bench |(do (def f (file/open "/proc/self/schedstat" :r ))
(def content (file/read f :all ))
(file/close f ))
iterations ) # 4894.832760 ns
# even without opening and closing the file, reading in Janet's slower than os/clock
(def f (file/open "/proc/self/schedstat" :r ))
(bench |(do (file/seek f :set 0 )
(def content (file/read f :all ))) iterations ) # 1802.511470 ns
(file/close f )
# Of course bench has some overhead, but it's amortized across iterations anyway
(bench (fn []) 10000000 ) # 42.030338 ns # from https://janet.guide/control-flow/
(defn calculate [expr ]
(match expr
[:add x y ] (+ x y )
[:subtract x y ] (- x y )
[:multiply x y ] (* x y )
([:divide x y ] (= y 0 )) (error "division by zero!" )
[:divide x y ] (/ x y )))
(calculate [:subtract 5 10 ])
# Note, it checks prefixes, so you must order things this way:
(match [1 2 ]
[x y z ] "three elements"
[x y ] "two elements"
[x ] "one element"
[] "no elements" )# read input at compile-time and create a custom func to check whether a number is in one of the
# given ranges. This turned out to be almost 4x faster than writing it as a func
# from https://abhinavsarkar.net/notes/2025-aoc-1/#day-2
(defmacro in-range? [n & _ ]
(def ranges (parse-input (slurp input-path )))
~(or ,;(map (fn [[s e ]] ~(<= ,s ,n ,e )) ranges )))(defn capitalize [str ]
(string (string/ascii-upper (string/slice str 0 1 )) (string/slice str 1 )))# due to 0 indexing, you will often want to add 1 to things:
(defn short-date [d ]
(let [{:year y :month mon :month-day d } d ]
(string y "-" (+ 1 mon ) "-" (+ 1 d ))))
# Makes os/date like 2025-12-25
(short-date (os//date ))
# From: https://codeberg.org/veqq/deforester/src/branch/master/deforester.janet
(defn- time-string
``Gives current time as ISO 8601 string: 2025-10-12T11:43:14 https://en.wikipedia.org/wiki/ISO_8601
This accounts for `os/date` 0-indexing month and days which are 1-indexed in ISO 8601.``
[]
(let [{:year y :month mon :month-day d :hours h :minutes min :seconds s } (os/date )]
(string y "-" (+ 1 mon ) "-" (+ 1 d ) "T" h ":" min ":" s )))# *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). # 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). # 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" # 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 # 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/$'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 ))# sorted allocates the full memory of its input
(def d2 (range 10000000 0 -1 )) # 87mb
(sorted d2 ) # now 167 mb # 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"