Janet Commonplace
Jul 2025 - Alex Alejandre

There are many accessors:

  • associative structures like (def m @{:k "val"})
    • (get m :k)
    • (in m :k) - n.b. it does not throw errors with tables & structs
    • (m :k)
  • indexed structures like (def a [:a :b :c])
    • (get a 0)
    • (in a 0) - throws error on failure
    • (a 0)
    • (0 a)
  • get
  • in

Pending Questions

  • what does math/next do? math/next 1 math/inf) just returns 1
  • why do product and sum exist? (product [1 2 3]) is the same as (* ;[1 2 3]). People suggested using them in these cases:
    • You could write (sum x) and (product x) instead of (reduce + 0 x) and (reduce * 1 x)
    • (map (fn [[a b]] (* a b))) -> (map product)
    • prefer (sum (seq [[game-id rounds] :pairs data :when (all true? ...)] game-id)) although slower, over:
(loop [[game-id rounds] :pairs data]
  (when (all true? ...)
      (set total (+ total game-id)))
total)
Solved Questions and Insights
  • import
    • https://github.com/janet-lang/pkgs/blob/master/pkgs.janet is the list of “core” imports without git links required
    • jpm install seemms to only move the source without package name so import only works through your entry file’s name. Thus you should name the main how you want the import API to be. Package name from project.janet seems to just be for jpm itself. To be clearer, in package blabla with source my-file.janet you (import my-file) which should have imports to the other files in the project
  • In the repl (defn main [& args] (+ ;args)) then (spit "test.janet" (make-image (curenv))) does not produce an image you can run with janet -i test.janet why not? could not find method :+
    • (defn main [waste & args] (print (reduce + 0 (map |(int/to-number (int/u64 $)) args))))
    • Both are possible: (reduce + 0 (map |(int/to-number (int/u64 $)) args)) or (reduce + 0 (map int/to-number (map int/u64 args)))
    • note that the first element passed is the filename janet is runniing, so you need to ignore that arg!
    • use (compose int/u64 int/to-number)
    • scan-number
  • how to create nested tables?
    • put-in
    • wrong solution: merge-into is destructive - (defn insert-verse (abbrev ch verse quote) (merge-into verses @{abbrev @{ch @{verse quote}}}))
    • made my own assoc-in here
(defn assoc-in [m ks v]
``Insert (and make) deeply nested tables, based on Clojure's.
Use like: (var t (table)) (assoc-in t [:a :b :c :d] "e")``
  (if (empty? ks)
    v
    (let [k (first ks)
          nested (or (get m k) @{})]
      (put m k (assoc-in nested (slice ks 1) v)))))
  • how do you make cond not print nil?
  • accepting cmd lin args via main into cond with (or (= 0 (length args)) (= "-h" args) (= "help" args)) to output help, why don’t they trigger?
    • args are a list, so use ; like ;args
  • how do you use if in each or loop on a map, when it outputs nil at the end? (each a {:a 1 :b 2 :c 3} (if (= 1 a) "yes" "no"))
    • (find |(string/has-prefix? "a" $) ["be" "cat" "art"])
  • How does image based development work? (load-image (slurp "test.jimage")) shows the image, but doesn’t bind the symbols etc. eval, repl, require, (run-context :read (slurp "test.jimage"))(fiber/setenv (fiber/current) (load-image (slurp "test.jimage"))) is also a no.
(defn restore-image [image]
  (loop [[k v] :pairs source]
    (put (curenv) k v)))

(restore-image (load-image (slurp "test.jimage")))
  • why does [1 2 3] output (1 2 3)? Are the tuple docs wrong?

    • It seems like the docs are wrong, but it doesn’t matter.
    • “The whole notion of tuple types seems a bit strange and it was just so we could have a little bit more syntactic variety and tuple constructor literals. Outside of things like the compilers and pretty printing, tuples of either type are considered equal. A bracket tuple will construct a parenthesized tuple, which should be considered the default.” - Bakpakin
  • cond vs. case?

    • use case if the important value is like an enum (particular thing, state)otherwisecond`.
  • when or if?

    • prefer when

Libraries