Clojure things I learn

Table of Contents

1 clojure

1.2 start an app

1.2.1 lein

lein new app foo lein repl lein run

1.2.2 deps.edn

1.2.3 cljs

lein new re-frame foobar +10x
edit shadow-cljs.edn add lib
npx shadow-cljs watch app
cider-connect-cljs local 7888 shadow-select :app .

1.5 shadow-cljs

https://jiyinyiyong.medium.com/a-beginner-guide-to-compile-clojurescript-with-shadow-cljs-26369190b786 Shadow-cljs is a tool, like make, or denom. Or lein. That builds packages. Like lein it does the building of the pipeline, but unlike lein, it also has the hot reload thing. So it's like lein+repl-with-hotreload.

Figwheel and figwheel-main are alternatives to it, but they are basically substitutes one for the other.

Clojurescript has shitloads of repls, https://lambdaisland.com/guides/clojure-repls/clojurescript-repls#org8300646 .At this point, I dont' know if shadow-cljs provides its own repl mechanism, or it's on you to say "shadow-cljs, I want you to use rhino as a repl". https://clojureverse.org/t/what-is-the-difference-between-figwheel-main-and-figwheel-sidecar/6832

1.6 etaoin

selenium-like. just from clojure/babashka.

1.7 string to uuid in postgres pg

(ns foo.bar
 (:import java.util.UUID))
(tdb/update-where! 'Product {:id id} :user-id (java.util.UUID/fromString new-window-id)))

1.9 indentation

1.10 lein test

lein test :only my.namespace/my-test lein test :only my.namespace

1.11 unalias

(ns-unalias ns 't)

1.12 socket repl

java -Dclojure.server.repl={:port 6666 :accept clojure.core.server/repl} -jar target/uberjar/myuberjar.jar
#notice in project.clj there's a :socket profile as well that i use for local dev

1.14 add conditionally to a map

(merge base-map
   (when name
      {:name name})
   (when address
      {:address address}))

similar to CL's `(:a 1 ,@(when foo `(:b 2)))

1.15 subsets

A set is a map with values equal to keys.

Creating a map from a set is trivial, you get {:a :a, :b :b ….}

maps can be converted to sets, and the elements are vectors of [k v]

(set [1 2])                               ; #{1 2}
(set {:a 1})                              ; #{[:a 1]}
(set {:a 1})                              ; #{[:a 1]}
(set/subset? (set {:a 1}) (set {:a 1}))   ; true
(set/subset? (set {:a 1}) (set {:a 2}))   ; false
(set/subset? (set {:a 1}) (set [:a 1]))   ; false
(set/subset? (set {:a 1}) (set [:a 1]))   ; false
(set [:a 1])                              ; #{1 :a}
(set [:a :a])                             ; #{:a}
(set [[:a 1]])                            ; #{[:a 1]}
(set/subset? (set {:a 1}) (set [:a 1]))   ; false
(set/subset? (set {:a 1}) (set [[:a 1]])) ; true
(set {1 :a})                              ; #{[1 :a]}
(into {} (set {:a 1}))                   ; {:a 1}

Even thought there might be a way to create a hash from a set like this (fun #{:a}) ; => {:a :a} that would mean that you can prepare a set and a hash that would be "equal", but from what I see above, conversions between maps and sets work neatly.

(set/subset? (set {:a 1}) #{:a})
(set/subset? #{:a}        (set {:a 1}) )
(set/subset? #{:a}        (set [:a]) )
(set/subset? (set [:a])   #{:a})
(set/subset? (set [:a])   #{:a})
...
(set/subset? (set {:a 1}) (set [[:a 1]])); true
(set/subset? (set {:a 1}) (set [:a 1])); false
{:a 1} (into {} [[:a 1]]); {:a 1}{:a 1}

Instead of checking #(every? (partial contains? %) [:a :b]) vs #(set/subset? #{:a :b :c} %) To check for presence of required keys as parameters, it's very easy and "apl-ish" (set/subset? (set [:first-name :last-name :company]) (:params request))

1.16 Where did I leave that stuff?

So, I'm debugging something, and I use the inspector to retain some value I want for later. d myvar will save that value as myvar in the current namespace you're in.

Later on, you move namespace and you want to get back that myvar but you don't remember where it was stored.

cider-apropos myvar will list you which ns have this defined. so you can go and fetch it from there.

1.17 Array of hashes to hash

(map (juxt :id identity) [{:id 1 :name "foo"} {:id 2 :name "bar"}])

1.18 Test private functions

If you want to test a private function, you can use the binding (which is always accessible), and call it.

(ns a.b-test
  (:use
   [clojure test]))

(deftest a-private-function-test
  (testing "a private function"
    (let [fun #'a.b/c]
      (is (not (fun nil))))))

ref and extended explanations: in stackoverflow.

1.19 managing exceptions in clojure

THere's some good discussion here in clojurians thread.

1.21 Start a compojure app

1.21.3 Deploy a compojure+ring app

lein new compojure hello-api-clj
cd hello-api-clj
lein ring uberjar # lein uberjar without 'ring' leaves you at the repl
java -jar target/hello-api-clj-0.1.0-SNAPSHOT-standalone.jar

OR

lein new compojure hello-api-clj
cd hello-api-clj
lein ring uberjar
java -jar target/hello-api-clj-0.1.0-SNAPSHOT-standalone.jar

If you were using tools.deps, there's no lein ring, or lein ring uberjar, https://practical.li/clojure-webapps/projects/banking-on-clojure/application-server-configuration.html shows how to do it there.

I'm going for compojure-template for now. No reason why, really.

1.21.4 create

lein new compojure my-stupid-app
lein ring server

lein-ring uses a fancy way to start the server, so you don't have an easy entrypoint before the server is kicked (integrant?, mount?, component?).

1.21.5 repl

if you want to start the repl when lein ring server, add in project.clj :nrepl {:start? true}.

lein ring server imports ring and ring-server/ring-server, which run the jetty server for you on start https://github.com/weavejester/lein-ring/blob/master/src/leiningen/ring/server.clj#L34-L35

But if you want control over it, add [ring "1.9.4"] explicitly to your project.clj, start with cider-jack-in, and in the main ns,

(use 'ring.adapter.jetty) ; if you don't require [ring ..] this is not available
(defonce server (run-jetty #'app {:port 3000 :join? false}))
(.stop server)

1.21.6 DB

1.21.7 Migrations

Migratus provides the lib AND the lein plugin.

You have to add next.jdbc AND the concrete driver for your db sqlite-jdbc in my case.

At this point to debug, I found that if you start from the console with lein ring server even if there is a repl, the process doesn't have the nrepl and cider helpers, so it's not really useful as a repl to be used from emacs. Interim solution: cider-jack-in and not have the server on. Have to figure that one asap.

1.21.8 Migrations in prod

To run migrations at the start of the app, lein-ring has a config :init and :destroy that run before and after loading the handler.

It seems it's the de-facto recommended way, but I still can't see how you would run it only on 1 of the deployed servers, or rollback if the migration fails.

1.21.9 uberjar

lein ring uberjar produces a jar that on start it actually runs the jetty thing. lein uberjar leaves you in the repl.

1.21.11 honeysql and next/jdbc

2 clojure exceptions

3 logging

timbre/log provides also spy and other cool stuff

4 concat trap

5 queryable runtime. "inspectability"

cider-apropos is super useful

(apropos "rename-keys")
(clojure.set/rename-keys)

(doc clojure.test)
(find-doc "dynamic")
for instance, this is our production jar:
MB_JETTY_PORT=3006 java -Dclojure.server.repl="{:port 50506 :accept clojure.core.server/repl}" -jar 0.40.0.jar
and then just a simple

% nc localhost 50506
user=> (apply require clojure.main/repl-requires)
nil
user=> (apropos "union")
(clojure.set/union)
user=> (apropos "password")
(clj-ldap.client/modify-password metabase.api.session/GET_password_reset_token_valid metabase.api.session/POST_forgot_password metabase.api.session/POST_reset_password metabase.api.user/PUT_:id_password metabase.api.util/POST_password_check metabase.driver.common/default-password-details metabase.email/email-smtp-password metabase.email.messages/send-password-reset-email! metabase.integrations.ldap/ldap-password metabase.integrations.ldap/verify-password metabase.models.database/protected-password metabase.models.user/form-password-reset-url metabase.models.user/set-password! metabase.models.user/set-password-reset-token! metabase.public-settings/enable-password-login metabase.public-settings/password-complexity metabase.util.password/active-password-complexity metabase.util.password/common-passwords-url metabase.util.password/verify-password)
user=>
  I once wondered how many things were added to clojure.core over the versions. After cloning the Clojure repo, and doing weird regex checks across history in git, I realised I could just do this:

user=> (->> (vals (ns-publics 'clojure.core))
  #_=>      (keep (comp :added meta))
  #_=>      (frequencies)
  #_=>      (sort-by #(Long/parseLong (subs (key %) 2)))
  #_=>      (run! println))
[1.0 434]
[1.1 38]
[1.2 45]
[1.3 16]
[1.4 8]
[1.5 11]
[1.6 8]
[1.7 19]
[1.8 1]
[1.9 27]
[1.10 8]

That's when I started to really "get" the value of the queryability. I then understood how easily I could implement my own version of clojure.repl/apropos.
Edit: fixed the sorting. (edited)

6 clojure internals

7 data-driven

8 simple stu halloway

9 cljs

10 core.async

11 java interop

12 tricky flatten

13 modern clojurescript

14 clojure integrant and the like

15 holy lambda!

  • lein new holy-lambda holy-lambda-example
  • nix-shell -p aws-sam-cli
  • aws login
  • AWS_PROFILE=my_profile sam local invoke ExampleLambdaFunction –region eu-west-1
  • AWS_PROFILE=my_profile sam local start-api –region eu-west-1
  • AWS_PROFILE=my_profile sam deploy –guided –region eu-west-1
  • http https://1232323abcd.execute-api.eu-west-1.amazonaws.com
  • AWS_PROFILE=rgrau sam delete –region eu-west-1

16 web dev

17 nested filtering, some, any , every , filter….

There's a weird nice relation between set/* functions, and using sets as containers for keywords, that are evaluated as functions.

filter + comp + set seems powerful

(->> @evts (map json/decode) (filter (comp #{"invoice.payment_failed" "charge.failed"} #(get % "type"))))

(select-keys (->> @evts (map json/decode) (group-by #(get % "type"))) ["invoice.payment_failed" "charge.failed"])

(filter (comp #{"foo" "bar"} :id) [{:id "foo" :n 1 } {:id "bar" :n 2 } {:id "baz" :n 3 }])

(when (#{"charge.failed" "invoice.payment_failed"} (get {"type" "charge.failed"} "type"))
  "yes")

(when (some #{"charge.failed" "invoice.payment_failed"} [(get {"type" "charge.failed"} "type")])
  "yes")

18 list all implementations of a defmethod

(keys (.getMethodTable ig/init-key))

19 find all public methods of a namespace

(ns-publics *ns*)

20 cli and tools and deps

21 dependency injection

22 clojure codebases

23 REPL large objects

  • profiles.clj
:global-vars {*print-length* 5
	      *print-level* 5}

  • overwrite default "to string"
(defmethod print-method martian.core.Martian [^martian.core.Martian v ^java.io.Writer w]
(.write w (str "#martian.core.Martian" (prn-str (select-keys v [:api-root])))))

In emacs you might still hit https://github.com/clojure-emacs/cider/issues/2856

(setq cider-repl-use-pretty-printing nil)

24 inspect last result in emacs

m-x cider-inspect-last-result or m-x cider-inspect-expr *1

25 packaging lein deps.edn

26 forms

27 state machine to parse files

rgm 12:42 AM I'm hunting for possible libraries or techniques for a kind of task that almost certainly has a googleable name, but I don't know it: I have plaintext e-mails coming in and want to act on their message bodies. They're not exactly parseable (ie. I don't think a grammar exists, so it doesn't feel like instaparse would work) and they'd be tedious to regex, though I suppose that might eventually work. Some parts of the body are (probably accidentally) TSV as a result of doing layout in plaintext.I guess I'm after … some data-defined way to establish zones relative to consistent landmarks, and say, ok, use this regex on the line 2 lines later and give me the groups. I can imagine doing a bunch of this with drop-until landmark, etc. working at the level of a line-seq.But I'm wondering if there are good approaches for almost-structured data that operates above lines and regexes and seqs and below formal grammars and parsing. hiredman

12:50 AM a state machine 12:53 (reduce (fn [[state-name data] line] (case state-name ..do stuff with line and data based on the content of line and the value of state-name and return a possibly new state…)) [:init {}] lines). like if you are in :init and see a blank line then move to :after-email-headers or whatever, if you are in :after-email-headers and you see the columns headers for the tsv then move to :accumulate-tsv, and in :accumulate-tsv add the lines to data until a line is blank

28 tap>

(-> {:q []}
	(update :q conj "a")
	(update :q conj "b"))

(def ml (atom {:q []}))
;;(remove-tap clojure.pprint/pprint)
(add-tap #(swap! ml update :q conj %))
(loop [[hd & tl] [1 2 3]]
  (tap> hd)
  (when tl
    (recur tl)))

get(dict, k) update(dict, k, f, args**) reset(dict, k, val, args**) inc update(d,'foo', inc)

29 clojure -Sdescribe

You'll see that there's

30 miniupdates in python

import copy
def update(d, k, f, *args):
    _c=copy.deepcopy(d)
    _c[k]=f(d[k], *args)
    return _c

def myappend(a,v):
    _a=a.copy()
    _a.append(v)
    return _a

d={'a': [1,2,3]}
update(d, 'a', myappend, 'hola')

def swap(f):
    return lambda x,y: f(y,x)

def swap(f):
    return lambda *args: f(*reversed(args))

list(update(d, 'a', swap(map), inc)['a'])

31 proj from scratche

  • create deps.edn -> {:deps …}
  • nix-shell -p neil
  • neil add build
  • neil dep add com.github.seancorfield/honeysql
  • 3 things to build an uberjar:
    • (:gen-class) in the main
    • def lib
    • add :main 'clojaws.core

Author: Raimon Grau

Emacs 26.1 (Org mode 9.1.9)

Validate