Fork me on GitHub

^ +1 to that. It's pleasantly surprising how cheap short-living garbage is on modern VMs.

Ivan Koz05:05:12

Why some of the functions defined this way?

 ^{:arglists '([x])
   :doc "Return true if x is a String"
   :added "1.0"
   :static true}
 string? (fn ^:static string? [x] (instance? String x)))


i'm not sure if this one in particular is due to this but you might be seeing workarounds to the bootstrap problem. If defn hasn't been defined yet then you can't use that form. And i imagine that defn uses string? so this must come before that

Ivan Koz06:05:13

@dpsutton thanks, seems logical

Eric Ihli06:05:45

This is a bit algorithmic-specific rather than Clojure-specific but I'm new to functional/clojure and that's making this extra difficult. I'm trying to create a seq of all combinations of sublists of a list. So [1 2 3] becomes [ [[1] [2] [3]], [[1] [2 3]], [[1 2] [3]], [[1 2 3]] ]

Idan Melamed06:05:24

Are you looking for a library that has a function that does that, or are you looking to write a function yourself?

Eric Ihli07:05:22

Idan, I'm curious how it's done. If there's a library, that's enough I could read the implementation and get a little bit of an understanding. Ivan, I looked at the combinatorics library and couldn't find the answer there. It's not quite subsets because order and position matters. For example, I can't have [1 3] or [2 1] in my result set from [1 2 3] input.

Idan Melamed07:05:39

Maybe partitions would be a better choice for you than?

Idan Melamed07:05:01

From the combinatorics GitHub page:

=> (combo/partitions [1 1 2])
(([1 1 2])
 ([1 1] [2])
 ([1 2] [1])
 ([1] [1] [2]))

Eric Ihli07:05:42

That's totally it! Thanks! I even freaking saw function in there about 2 hours ago and it didn't register that it was what I was looking for and I spent the next two hours trying to figure it out.

Eric Ihli07:05:29

Ah I think I know what happened. I scanned the list of functions in combinatorics, I saw "partition" and thought that might be it, so I googled "clojure partition" and saw and that's totally not it.

Eric Ihli07:05:45

Bah. No. Even that's not quite right.

(combo/partitions [1 2 3])
(([1 2 3]) ([1 2] [3]) ([1 3] [2]) ([1] [2 3]) ([1] [2] [3]))
I can't have ([1 3] [2]) in there. I can just filter out any flattened sublist that isn't in the correct order.

Eric Ihli07:05:21 a quick fix. Still eventually want to figure out the proper algorithm.

Ivan Koz07:05:18

@ericihli permutations creates a lazy sequence of calls to iter-perm function in one or another way

Ivan Koz07:05:22

well, i was a bit wrong here, authors redirect us to a book

Most of these algorithms are derived from algorithms found in Knuth's wonderful Art of Computer Programming books (specifically, the volume 4 fascicles), which present fast, iterative solutions to these common combinatorial problems.  Unfortunately, these iterative versions are somewhat inscrutable.  If you want to better understand these algorithms, the Knuth books are the place to start.


Hi all. How to require spec defined in another file (namespace) with

(s/def ::my-string string?)


This seems to work:

(s/valid? ::hs/my-string word)
is the alias I gave to the spec namespace. Is this the way I shoud refer to spec def in another namespace?


TL;DR - Need help with deploying a shadow-cljs project to heroku. Would anyone be able to answer some questions about deployment to heroku? I have a test project here: I’m trying to deploy it to heroku but I’m not sure what the incantations are. * What build packs do I need? Ruby, Node, Java - all of them? * What should the Procfile contain? * Who the heck sent me down this wacky path in the first place?! 👉 this guy @bobnadler (only slightly kidding 😉 ) If anyone has the time to assist (or sync on a zoom call) - that would be great!

Jakub Holý (HolyJak)20:05:34

Not sure. I guess you need Java to be able to build it (since cljs/shadow uses Google Closure compiler written in Java) and Node to run it. You could ask in #shadow-cljs, perhaps.

👍 4

Where should we ask jdk questions? I've been trying out adoptopenjdk with openjdk 11. I am also exploring graalvm so installed the community edition and put that in my path. Now (on linux debian) whichever export statement I have last in my .profile turns into my default jdk. If graalvm path is last then my java -version is java 8 graalvm with no mention of adoptopenjdk. vice versa with java 11 if i have adoptopenjdk last. How does this affect my clojure software development? Are these two going to coincide peacefully or will my various projects be all screwed up depending on which one I used to start a project or run a project? Sorry if this is too convoluted.


you can have these coexist, you can treat them as separate JDKs


Some parts of graal like native image are not officially supported by clojure


ok cool. I think I will just keep adoptopenjdk 11 as my default and try to bring graalvm to the forefront whenever I want to explore creating a native binary. Which is different than the "native image" thing you just mentioned right? I was thinking the whole benefit of clojure folks trying graalvm was to compile to native binaries.


native-image is "Not Java"


you're going to run into brick walls every other minute


lots of restrictions, some parts of the JDK are not supported


if you want something that is supported try jaotc, it's part of the JDK


both are a pain in the ass to use, but jaotc is Real Java


it produces native binaries


Has anyone here had a good experience with the service? I think I am going to drop for the membership but just want to hear a testimonial to put my mind at ease first


I've personally have had a great experience with it. Eric is super responsive too and always willing to help out. It helped me to understand some hard concepts like reduce, destructuring, for, etc. I haven't got through much yet but I'm liking what I've seen so far.


Thanks! I think it will be a worthwhile investment. Good to hear that it seems to be well curated


Yeah I think he has a good feel for how to present the material in an understandable way. The clojure community is super supportive and there are great resources but I think it's hard to put yourself back in the beginner's mind or even intermediate mindset and explain things in simpler terms. I think he finds that balance.


Sometimes I get some great help here but it's like a mile above my head. I haven't felt that with his tutorials

bowie 4

I also think that one is pretty solid - definitely the best one for beginners. Also largest and still growing - unlike (sadly)

Chris Swanson15:05:34

anyone got a minute to assist with cider nrepl issues ?


What's the issue? I'm not at my machine but I think usually the issues are related to versioning

Chris Swanson15:05:09

i'm using nrepl 0.6.0 and cider/cider-nrepl "0.22.0.beta1"

Chris Swanson15:05:18

which seem to be the latest

Chris Swanson15:05:03

my emacs cider is 0.22.0snapshot

Chris Swanson15:05:14

which seems to be most recent versions of everything

Chris Swanson15:05:34

i'm trying to embed a repl , build and run an uberjar, and connect to it

Chris Swanson15:05:05

the connection from emacs happens (with cider-connect-clj)

Chris Swanson15:05:19

and it doesn't complain about version mismatch or anything

Chris Swanson15:05:32

but the clj app throws a bunch of exceptions

Chris Swanson15:05:46

i launched the repl server like this:

(:require  [nrepl.server :as nrepl-server] 
  [cider.nrepl :as cider]) 

 (nrepl-server/start-server :port 7888 :handler cider/cider-nrepl-handler) 

Chris Swanson15:05:14

but it can't find namespaces , hangs on eval buffer, throws exceptions like this:

Chris Swanson15:05:48

ERROR: Unhandled REPL handler exception processing message {:op complete, :ns user, :symbol co, :context :same, :session 6e561eda-b8f2-400d-b1c6-67485763e6d5, :id 10}

Chris Swanson15:05:59

any thoughts ?


can you share more of the stacktrace?

Chris Swanson15:05:48

the only unusual thing i'm doing is including compiled classes for grpc stuff

Chris Swanson15:05:28

ERROR: Unhandled REPL handler exception processing message {:op out-subscribe, :session 4c15cf3a-fd34-4464-9c70-8c07c8367587, :id 5}
        at clojure.core$deref_future.invokeStatic(core.clj:2300)
        at clojure.core$deref.invokeStatic(core.clj:2320)
        at clojure.core$deref.invoke(core.clj:2306)
        at cider.nrepl$wrap_out$fn__10724.invoke(nrepl.clj:290)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at cider.nrepl$wrap_enlighten$fn__10686.invoke(nrepl.clj:162)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at cider.nrepl$wrap_refresh$fn__10736.invoke(nrepl.clj:336)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at cider.nrepl$wrap_tracker$fn__10772.invoke(nrepl.clj:437)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at nrepl.middleware.session$session$fn__10452.invoke(session.clj:272)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at nrepl.middleware.print$wrap_print$fn__10259.invoke(print.clj:234)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at cider.nrepl$wrap_slurp$fn__10654.invoke(nrepl.clj:94)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at nrepl.server$handle_STAR_.invokeStatic(server.clj:18)
        at nrepl.server$handle_STAR_.invoke(server.clj:15)
        at nrepl.server$handle$fn__10483.invoke(server.clj:27)
        at clojure.core$binding_conveyor_fn$fn__5739.invoke(core.clj:2030)
        at java.base/
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(
        at java.base/java.util.concurrent.ThreadPoolExecutor$
        at java.base/
ERROR: Unhandled REPL handler exception processing message {:nrepl.middleware.print/stream? 1, :nrepl.middleware.print/print cider.nrepl.pprint/pprint, :nrepl.middleware.print/quota 1048576, :nrepl.middleware.print/options {:right-margin 70}, :op init-debugger, :session 4c15cf3a-fd34-4464-9c70-8c07c8367587, :id 6}
        at clojure.core$deref_future.invokeStatic(core.clj:2300)
        at clojure.core$deref.invokeStatic(core.clj:2320)
        at clojure.core$deref.invoke(core.clj:2306)
        at cider.nrepl$wrap_debug$fn__10680.invoke(nrepl.clj:136)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at cider.nrepl$wrap_inspect$fn__10706.invoke(nrepl.clj:199)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at cider.nrepl$wrap_trace$fn__10766.invoke(nrepl.clj:419)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at cider.nrepl$wrap_macroexpand$fn__10712.invoke(nrepl.clj:243)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at cider.nrepl$wrap_spec$fn__10748.invoke(nrepl.clj:374)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at nrepl.middleware.load_file$wrap_load_file$fn__10366.invoke(load_file.clj:81)
        at nrepl.middleware$wrap_conj_descriptor$fn__10059.invoke(middleware.clj:16)
        at cider.nrepl$wrap_content_type$fn__10648.invoke(nrepl.clj:82)

Chris Swanson15:05:15

it goes on for days like that, i can post it all if that'd help


do you ever get to a java.lang.ClassNotFoundException?

Chris Swanson15:05:01

nope, all null pointer exceptions


gotcha - I was seeing if this was a nrepl-related issue that was fixed relatively recently with a new snapshot release, but it seems like that's not the case here. unfortunately I don't know enough to help further!

Chris Swanson15:05:32

no worries, thanks for giving it a try :thumbsup:

Chris Swanson15:05:24

i can fall back to my old workflow - just the classpath stuff with grpc/proto has me recompiling and it kills the repl workflow


so I’m trying to re-create your error, and I when I use cider-nrepl 0.21.1, it works fine

Chris Swanson15:05:35

i only started having trouble when i wanted to embed the repl in the jar and connect that way, instead of cider-jack-in


I’m not uberjaring anything tho

Chris Swanson15:05:00

what if you build uberjar and launch embedded repl server ?

Chris Swanson15:05:10

let me give it a try with 0.21.1 also

Chris Swanson15:05:52

do you know how to force emacs to use older version of cider?

Chris Swanson15:05:08

WARNING: CIDER 0.22.0-snapshot requires cider-nrepl 0.22.0-beta1, but you're currently using cider\
                                                                                                   |-nrepl 0.21.1. The version mismatch might break some functionality!

Chris Swanson15:05:45

same exceptions and behavior (frozen repl) , regardless


I would bring this up in #cider


How can I run single .clj file without Leiningen? I tried java -cp clojure.jar clojure.main test-code.clj but got an error: "Error: Could not find or load main class clojure.main" I tried to find clojure.jar (and even clojure*.jar) on my machine and there is none. I installed Clojure with Leiningen (downloaded from official site and followed the docs)

Alex Miller (Clojure team)15:05:28

for lein, I believe you can do lein run -m clojure.main test-code.clj

Alex Miller (Clojure team)15:05:21

lein uses the Maven dependency cache in ~/.m2/repository and so the main clojure jar will be buried in that cache, along with any other dependencies you use.


I don't want to use Leiningen in this case because of it's startup time

Alex Miller (Clojure team)15:05:53

the main clojure jar has two dependencies as well, so it's slightly more complicated to use it directly


would clojure /path/to/file.clj not work? Am I mistaken?

Alex Miller (Clojure team)15:05:13

you may be interested in the clj tool as well


@mitchell_clojure it can work if Clojure is installed with standard package manager

Alex Miller (Clojure team)15:05:51

clj is a dependency downloader, classpath constructor, and wrapper around clojure.main

Chris Swanson15:05:43

i think you can use boot and shebang notation to kick off a clojure file but the clojure.jar still needs to load

😲 4
Chris Swanson15:05:03


#!/usr/bin/env boot

(defn -main ....)

Chris Swanson15:05:14

then chmod +x it


Ok, I did sudo apt install clojure which gave me clojure binary and it's version 1.9. Now I can do clojure my-code.clj


I didn't know Ubuntu has the latest Clojure in the repo

🙂 4
Alex Miller (Clojure team)15:05:41

I'm not sure what that clojure script you're getting is

Alex Miller (Clojure team)15:05:25

can you do clojure -Sverbose ?

Alex Miller (Clojure team)16:05:23

if that works, then that's cool. if it doesn't, then that's some non-official script being included with the distribution.

Chris Swanson16:05:04

also, since you mentioned startup time; if you don't need jvm ecosystem, you may be interested in cljs/node alternative planck for scripting

Chris Swanson16:05:13

actually to correct myself planck doesn't depend on node


Right, Planck uses JavaScriptCore, Lumo uses V8, but effectively is a special build of Node

✔️ 4

BTW, is Clojure on .NET mature? Can it work on .NET Core ?


you're asking about the Clojure CLR project?


oh, yes. Just forgot correct name


i haven't used it, myself


@gr.evocatus If you're up for listening to a podcast, has the best recent overview of everything you might want to know about it before using it

❤️ 4

Thanks 🙂

Alex Miller (Clojure team)16:05:54

ClojureCLR is basically up to date with the jvm version (David Miller was working on 1.10.0 compatibility)


It's great !


Looks like there are enough differences between JVM and JS to be extremely careful while prototyping on Planck/Lumo. Even numbers are represented differently (JS doesn't have actual Long Integers)


somewhere there is an incomplete and always growing list of differences between clojure and clojurescript


Does anyone have tips for profiling slow leiningen start up times? I am on a fresh machine, running same java version and lein version as rest of my team, yet start up times are almost double than theirs.


I had to set a large timeout in my profiles.clj to load the same project, otherwise it times out


the first thing to do is to examine the differences between your setup and other people on the team's setup


configuration from sources that don't often get checked in, like a user.clj or profiles.clj, are likely contenders


Hm yeah I did compare profiles.clj


i wonder if maybe it's a network issue with our private maven repo


what timeout option do you need to set?

Alex Miller (Clojure team)17:05:18

there is a known performance issue with the latest version of the jdk that has a drastic perf impact if you are using a user.clj file


  {:repl-options {:timeout 120000}}}
in my profiles.clj

Alex Miller (Clojure team)17:05:47

what's your java -version and do you have a user.clj file?


that has nothing to do with fetching deps from maven


fetching deps happens after loading right? I do not have a user.clj, using java 1.8.0_202 and lein 2.9.1

Alex Miller (Clojure team)17:05:38

java 1.8.0_202 is the first Java 8 build with the issue (also 11.0.2, 12)

👍 4
Alex Miller (Clojure team)17:05:22

but if you don't have a user.clj file, this is probably not the issue.


lein runs two jvms, one for lein one for your project, the repl client runs in the lein jvm and the server runs in the project jvm


that timeout is how long the client waits for connecting to the server

👍 4

so that indicates the project jvm is slow to start, which sounds like you have a user.clj somewhere


don't think i have a user.clj, i never created one


but let me verify


you can verify if there is a rogue one on your classpath by running ( "user.clj")


thank you

😁 4

if you don't find one, it's probably just bloated dependencies or plugins


there's definitely bloat


oh yeah, if you have any plugins get rid of them


but not sure why my machine would take more than twice as long to start up


plugins can literally do anything


so they could just sleep for 10 seconds if the hostname doesn't match a regex


there is a user.clj in the module itself


checked with ( "user.clj") per @ghadi I think that solves it


what's in the file?


and what do you mean by "module"


sorry the project directory


project/project.clj, project/env/dev/user.clj


nix it and watch the performance come back

Alex Miller (Clojure team)17:05:22

we are working on a dodge around this issue for 1.10.1


excellent, thanks everyone for the help


should be able to drop in the 1.10 beta and see an improvement if so, right?

Alex Miller (Clojure team)17:05:27

yes, 1.10.1-beta2 is available now

👍 4

Is it possible to use something like pattern matching in multimethods (or build a new multimethod implementation around pattern matching)? I'd like to do something like the following:

(defmatchmulti mm [a b] [a b])
(defmatchmethod mm [1 2] [a b] :a1)
(defmatchmethod mm [_ 2] [a b] :a2)
(defmatchmethod mm [1 _] [a b] :a3)
(defmatchmethod mm :default [a b] :a4)

(mm 1 2) ;; => :a1
(mm 3 2) ;; => :a2
(mm 12 104) ;; => :a4
I looked at core.match, but I'm not sure there's any way for me to build a set of patterns dynamically. (edited for syntax, added function bindings)


I'm working on implementing something that translates underscores to ::no-match and does the comparison at runtime, but it seems a bit brute-forcey


Here's what I came up with:

(defn create-match-multi
  (let [m-atom (atom {})
        (fn [d-value target]
          (not= target :default)
          (all (mapv #(or (= %2 ::skip) (= %1 %2)) d-value target))))]
      (fn [& args]
        (let [d-value (apply dispatch args)
              m @m-atom
              m-pairs (seq m)
              n-pairs (count m-pairs)]
           (loop [idx 0]
             (let [m-pair (nth m-pairs idx)]
                (matches? d-value (first m-pair)) (second m-pair)
                (< idx (dec n-pairs)) (recur (inc idx))
                (contains? m :default) (:default m)
                :else (throw (js/Error. (str "Failed to dispatch " args))))))
      {:methods m-atom})))

(defn add-match-method
  [mm pattern f]
  (swap! (:methods (meta mm)) assoc pattern f))

(defmacro defmatchmulti
  [sym bindings & body]
  `(def ~sym (create-match-multi (fn ~bindings ~@body))))

(defmacro defmatchmethod [mm pattern bindings & body]
  (let [pattern (if-not (= pattern :default)
                  (mapv #(if (= % '_) ::skip %) pattern)
   `(add-match-method ~mm ~pattern (fn ~bindings ~@body))))


This seems to work fine, but again, doesn't use any fancy pattern matching algorithm, it just iterates through the maps's keys.

Kari Marttila18:05:44

Clojure Gurus! I yesterday asked how to convert this:

[{:pgid {:S "1"}, :pgname {:S "Books-local-aws"}} {:pgid {:S "2"}, :pgname {:S "Movies-local-aws"}}]
to this:
{"1" "Books-local-aws", "2" "Movies-local-aws"}
I was asking whether there is some solution that doesn't use recur or reduce. I got two answers: Solution 1 (my original solution elaborated a bit with Clojure gurus):
      [mymap item]
      (assoc mymap
        (-> item :pgid :S) (-> item :pgname :S)))
and: Solution 2:
(->> items
         (map (juxt (comp :S :pgid) (comp :S :pgname)))
         (into {}))
Do real Clojure gurus favor to find a solution based on the standard library (e.g. using map + juxt as in solution 2) or are solutions 1 and 2 equally good?

Lennart Buit18:05:49

The first I find easier to read, but the second is cooler (I didn’t know juxt!). I like to go with the code that is the “least surprising” so that would be 1.

Lennart Buit18:05:43

That said, wrap your implementation in a function, add a docstring, write a test, and they are equal from a usage perspective 🙂

Kari Marttila18:05:44

... and is there some way to "reverse-engineer" some expressions like

(juxt (comp :S :pgid) (comp :S :pgname))
to see what kind of function gets created? It took me some time and mental effort to really understand what was happening there. 🙂


@kari.marttila in my experience, reading the doc strings for juxt, comp, partial, and apply, then translating as many functions as I can into "point free" style as I can with repl experimentation


the idea with point-free is that you eliminate variable names from the code, and those functions in particular allow a specific style of point-free code when used together


and after enough experimentation and forced examples, it gets easier to recognize cases where they will simplify your code


(and along with that, it gets easier to recognize the point free idioms when reading code)


also, learning about lambda calculus might help if you like higher math, or are fond of that style of reasoning

Kari Marttila18:05:10

Ok. Thanks. Practice, practice, practice.

Kari Marttila18:05:54

Work hard Kari!

Lennart Buit18:05:46

haha, and take time in a hammock every now and then 🙂!

Kari Marttila18:05:57

All work and no play makes Kari a dull boy. 

Kari Marttila18:05:03

What's hammock?

Kari Marttila18:05:05

Hammock = a bed made of canvas or rope mesh suspended from two supports by cords at both ends. ?


right, Rich Hickey coined the term "Hammock Driven Development" - his talk about it is good

Kari Marttila18:05:55

All right! I'm gonna watch it this night!


my sloppy version is take the time to daydream and think about the implications and look for connections and simpler versions of things before writing too much code

Kari Marttila18:05:50

It's a bit funny. I remember reading that term every once in a while in this site (and others) - I always thought that you were referring to Hammond (electric piano) (- I'm not native English speaker). I always thought that it means something like "Enough coding, I go play piano". 🙂


haha, that interpretation actually makes sense even

Kari Marttila18:05:54

Yep. I only now bothered to check the word in the dictionary. Never assume anything - always check the dictionary. 🙂

Kari Marttila18:05:22

Actually my English teacher at school told us to start reading English novels - and not to use dictionary - "Try to figure out the meaning of a new word from the context". So, I wasn't sure of the word and I somehow must have used the context and interpreted the meaning to refer to electric piano (which I mixed : hammock - hammond).


I think a mixture of "make good guesses from context and see how far you get" and "look up the real definitions of things you think you know" will get you really far in most subjects :D


I have learned a lot about clojure (and programming in general) by intentionally researching things I supposedly know already

Lennart Buit20:05:20

In university, I learned a lot from Compiler Construction. I had learned Java in my first year, but didn’t quite understand it until I had to make a compiler targetting java byte code

Lennart Buit20:05:49

Point being, sometimes you think you understand something, but there is often so much more below

Kari Marttila18:05:00

BTW. These video recommendations are really good. Every time someone recommends a Clojure related video I send an email to myself to remind me to watch the video (my inbox - a kind of task list). E.g. Stuart Halloway's REPL Driven Development was astonishing. I also started to write my REPL stuff in a file instead of REPL editor. I already have a bunch of various expression experiments in my scratch file. All these expressions would have disappeared like tears in rain if I had used REPL editor - now they are eternally in my scratch file for me to browse them through later time to see if I have done something similar in the past.

Lennart Buit18:05:05

you can also make watch lists on YT

Kari Marttila18:05:36

Great! Another email sent to me!


@kari.marttila I usually accompany reading the docstrings with exercising the expressions with simplest possible examples.

user> ((juxt inc dec) 1)
[2 0]
From there it’s easier to jump to
user> (map (juxt inc dec) [1 2 3])
([2 0] [3 1] [4 2])
…and finally
user> (into {} (map (juxt inc dec) [1 2 3]))
{2 0, 3 1, 4 2}

Alex Miller (Clojure team)19:05:22

for juxt, I find it's helpful to think of it as opposed to map - map applies the same function to many pieces of data. juxt applies many functions to the same data. (with the difference that juxt returns a function rather than doing the work itsefl)

👍 8

Good point that juxt can apply any amount of functions

user> ((juxt inc dec str -) 1)
[2 0 "1" -1]


Here is a handy usage of juxt in a higher order function that I keep in my utility belt:

(defn index-by
  ([idx-fn coll]
   (index-by idx-fn identity coll))
  ([idx-fn value-fn coll]
   (into {} (map (juxt idx-fn value-fn)) coll)))
I use it often when I have a vector of entities and I want to have a map of ID’s to the entities for easy access:
(def ms [{:id 1 :name "kissa"} {:id 2 :name "koira"}])

user> (index-by :id ms)
{1 {:id 1, :name "kissa"}, 2 {:id 2, :name "koira"}}
3-arity version let’s you manipulate the values as well
user> (index-by :id :name ms)
{1 "kissa", 2 "koira"}
And with some comp magic you can achieve more complex manipulation of keys and values
user> (index-by (comp str :id) (comp string/upper-case :name) ms)
{"1" "KISSA", "2" "KOIRA"}
…and so on. Super powerful!

👍 8

As a side note index-by is a ‘less generic version’ of core function group-by


i really think that index-by should throw on duplicate keys


side note, I know


Yeah there’s a caveat. Good point. 😉


the version I use:


(defn index-by
  ([keyf coll]
   (index-by keyf identity coll))
  ([keyf valf coll]
   (reduce (fn [m v]
             (let [k (keyf v)]
               (if (find m k)
                 (throw (ex-info "Duplicate key" {:k k}))
                 (assoc m k (valf v)))))
  ([keyf valf mergef coll]
   (reduce (fn [m v]
            (let [k (keyf v)]
               (if-let [entry (find m k)]
                 (assoc m k (mergef (val entry) (valf v)))
                 (assoc m k (valf v)))))


That’s definitely more solid but but… the aesthetics… 😄


i'm not trying to date my code 😂

Dave Compton20:05:26

I'm following these instructions : to get a repl running in a browser. Everything seems to work except that I don't get a repl prompt in the browser window. Does anyone have any thoughts on what might be going wrong or how to diagnose the problem?


I didn't think that was supposed to have a repl in the browser itself - usually a cljs repl is in a terminal and talks to the browser over a socket


> After a couple of seconds you will see Hello world! print at the terminal and you will be given a REPL prompt. Try evaluating some expressions like the following: to me, that means "enter these things into the terminal"

Dave Compton20:05:05

I do see the "Hello world!" printout but no prompt at the terminal either. When I type into the terminal I get no repsonse.

Dave Compton20:05:06

I would be happy with a repl in either the browser or the terminal.

Dave Compton20:05:52

The message in the browser window says "This page hosts your REPL and application evaluation environment. " which gave me the impression that the REPL would appear on the page but I don't really care one way or the other.


The "developer console" built into the browser is like a JS repl, but it's not usually the repl we're talking about when talking about clojure repls. We'll usually be referring to the terminal repl (or one connected from your IDE)


But if you follow the quick-start guide, you should have gotten a prompt at the terminal repl

Dave Compton20:05:10

no prompt at the terminal repl.

Dave Compton20:05:21

just a "Hello World!" message

Dave Compton20:05:13

and a browser window popped up as the instructions said would happen.


does it say the clojurescript version, right under the "Hello world!" message in the terminal?

Dave Compton20:05:07

The "hello world!" message prints again if I refresh the browser window.


Are there any messages in the developer console? (the one within the browser)


Did you remember to pass the flag to enable the repl at the end? clj --main cljs.main --compile hello-world.core --repl

Dave Compton20:05:00

no messages in the browser console.

Dave Compton20:05:08

here's what I see in the terminal:

Tomasz Rojek20:05:32

Hi. I have two vectors: 1. - [ [Z1 X1 C1] [Z2 X2 C2], .....[Zn Xn Cn] ] 2. - [ [Q1] [X1] [W1] [Q2] [X2] [W2] [Qm] [Xm] [Wm] ] m != n The names Z X C .... are fields with some type (type in domain, not in programming - all of theme are strings) I want to combine those two vectors into one if they match pattern that field X in first vector is the same a field X in the second vector. I'm looking for a hint how to do it not for a solution. Do any one know how to do it in Clojure?

Dave Compton20:05:59

( I think slack didn't like what I typed in )

Dave Compton20:05:57

/repos/gitnc/hello-world$ clj --main cljs.main --compile hello-world.core --repl Hello world! Hello world!


merge-with is a start


@davecompton7 That's strange :thinking_face:

Tomasz Rojek20:05:07

In OO language (or just not Clojure) I will make two loops (O(n2)) compare values and merge theme...

Dave Compton20:05:28

Is there any kind of error log or something along those lines that I can look at?


you could check the javascript console, that should be somewhere in the browser UI depending on which one you are using

Dave Compton20:05:44

I'm a little at a loss for where to go from here.


Well, the ordering of things don't appear to be guaranteed actually, so I'm not sure if you'll want to start with merge-with there

Tomasz Rojek20:05:09

@john (mege-with function_which_check_condition vector1 vector2) - will it be something like this?


@davecompton7 Have you tried starting fresh from another folder entirely?

Dave Compton20:05:40

you mean another directory in the filesystem ?


@tomek423 Yeah, but that'd assume your values in both vectors are aligned


I'm not sure how you plan to match up which groups belong to which, between vectors


@davecompton7 yeah, just to make sure it's a fresh development environment

Tomasz Rojek20:05:53

@john What do you mean by aligned? They are always on the same indexes in both files.


So then, you should be able to partition the second vector by 3, and then feed both into merge-with

Tomasz Rojek20:05:15

I made vectors from csv file.

Dave Compton20:05:17

I have not tried that yet. I will. I did try deleting the "out" folder that clj created in my "hello-world" folder and then starting again but that didn't help. I'll try starting fresh from a new folder now and let you know if that makes a difference.


I'm not sure about how W and C are playing together there though

Tomasz Rojek20:05:57

@john Thank you I will try to play with (merge-with ...)


@davecompton7 there's also a ./.cpcache folder that you can clear out too

Tomasz Rojek20:05:59

@john As a result I want to get [[Z1 X1 C1 Qx Wx] [Z2 X2 C2 Qy Wy] ....]

Tomasz Rojek20:05:07

@john The X is just an email address, and in one vector I have some data an in another I have another part of data related with the first vector by email value.


@tomek423 clojure.set/project isn't the exact thing you want, but I think it would do most of your work for you if you shape your data into what it can use

Dave Compton20:05:13

started over completely from scratch and it made no difference.


@davecompton7 you may have some local environment issues. I gotta run though!

Tomasz Rojek20:05:11

@noisesmith I just looked at the examples of clojure.set/project and they are based on hashMap. Will it work on vectors?


sorry - I was thinking clojure.set/index but I typed project instead by mistake


but you still likely need to do some data transformations to make it work (I think it wants sets of maps)

Dave Compton20:05:27

thank you for giving it some thought. Does anyone else have any thoughts on what the problem might be ( or how to diagnose it ).


I just tried the instructions in a fresh directory on a Mac and here's the output

(! 667)-> clj --main cljs.main --compile hello-world.core --repl
Downloading: org/clojure/clojurescript/1.10.516/clojurescript-1.10.516.pom from 
Downloading: org/clojure/clojurescript/1.10.516/clojurescript-1.10.516.jar from 

ClojureScript 1.10.516
but it took a long time between the Downloading messages and the blank line, version, and prompt appearing.


And I just waited, before trying to reload the page.


And you don't get the prompt etc until after the browser has opened and the page has rendered (showing the "Welcome to the ClojureScript browser REPL." page)... and then it takes a while before the prompt appears.


In particular note that you shouldn't get Hello World! in the terminal until after you reload the browser -- and you shouldn't reload the browser until after you get the prompt.


What O/S are you on and what browser are you using by default? Do you have non-default security settings on the browser that might be preventing the connection to Clojure/ClojureScript?

Dave Compton21:05:16

I'm running in a docker container that is based on Ubuntu 16.04. The OS on my computer is also Ubuntu 16.04.


And the browser...?

Dave Compton21:05:57

The brower that I'm using is chromium.

Dave Compton21:05:33

I have not changed anything as far as security settings go. At least not intentionally.

Dave Compton21:05:54

here's what I see in the terminal including starting from a fresh start with a new hello-world directory:

Dave Compton21:05:03

(bear with me... struggling with copy/paste into slack )

Dave Compton21:05:31

I've waited 20 minutes for now for the repl prompt. No reloading of the browser but I do see a single "Hello World!"


OK, I think this might be a timing issue. If you're getting Hello World! before you get a prompt then I suspect something in the initial ClojureScript setup isn't happening before your compiled hello_world.cljs file is run and so you are not getting the ClojureScript connection and therefore no connected REPL.


Can you try this natively on your Linux machine, outside Docker?

Dave Compton21:05:15

I tried it. I'm getting some kind of java (?) error:

Dave Compton21:05:39

(this kind of problem is why I usually try to isolate my development environment using docker or a vm )

Dave Compton21:05:03

figured it out ( or at least a workaround ). If I start the browser before running the clj command then clj connects to the running browser and I get a repl in the terminal.


What does java -version show? That error is a class file version mismatch.


Ah, that does sound like a timing issue then!


Clojure/ClojureScript requires JDK 8+ now -- which is probably your problem trying to run it locally.

Dave Compton21:05:03

dave@peach:~/repos/gitnc/hello-world$ java --version Unrecognized option: --version Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Program will exit.


Sorry, java -version -- one -


I always forget that java isn't like other *nix commands 🙂

Dave Compton21:05:53

dave@peach:~/repos/gitnc/hello-world$ java -version Error occurred during initialization of VM java.lang.Error: Properties init: Could not determine current working directory. at java.lang.System.initProperties(Native Method) at java.lang.System.initializeSystemClass(


Sounds like you're trying to run the command in a directory you deleted in another window...

Dave Compton21:05:17

java -version java version "1.7.0_67" Java(TM) SE Runtime Environment (build 1.7.0_67-b01) Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode) dave@peach:~/repos/gitnc/hw3$

Dave Compton21:05:30

and yes, it looks like the wrong java

Dave Compton21:05:25

but at this point it appears to be working under docker so I'm not too worried about getting it to work on my "real" computer.

Dave Compton21:05:16

in fact, can you tell me how to remove the clojure that I just installed ?


Ask in #tools-deps -- I'm not sure what the official process is for uninstalling clojure/`clj` since I've never needed to do it.


I suspect it's a manual process of deleting things from certain folders but I've no idea where it even puts stuff on Ubuntu.

Dave Compton22:05:46

ok. Thanks for your help. Your "timing" comment got me on the right track.


The downside to using Docker 🙂

Dave Compton20:05:58

Really, the "how to diagnose it" is the more important part of my question.


why is intellij the size of wisconsin?


I really hope that’s rhetorical.


like "boy, is that delicious?"


I’d generally right that as an exclamation, with ‘!’ instead of ‘?’


Is there a succinct and idiomatic way of pulling static java methods into a clojure namespace as functions. Tried the following (def int2hex (partial Integer/toHexString)) Unable to find static field: toHexString in class java.lang.Integer


I just meant rhetorical in the sense that it doesn’t expect an actual answer


@kbosompem I don’t think partial works with static Java methods. you might have to use #(Integer/toHexString %)


because (Klass/staticMethod …) is actually a macro

Ivan Koz22:05:42

@kbosompem idiomatic way is to use it directly


@penryu memfn works on instance methods but not static methods and its a macro

Alex Miller (Clojure team)22:05:57

memfn usually introduces reflection fyi


that's an interesting question. I'm not sure its actually a macro. Fun to go spelunking why this is the case. Its thinking its a field and rightly not finding it

Alex Miller (Clojure team)22:05:38

It’s not a macro, it’s the . special form

Alex Miller (Clojure team)22:05:13

Which works with both fields and methods and will report as a field error if not found

Alex Miller (Clojure team)22:05:49

Back to the original question, no there is no static import

👍 4

Well, in the sense that (Integer/toHexString ...) expands to (. Integer toHexString …). But the expanded form also can’t be partialed, so… as @alexmiller: no static import — invoke directly or wrap in a lambda.

👍 4
Alex Miller (Clojure team)23:05:51

It is possible to macro the creation of anonymous functions for multiple static methods in one step

Alex Miller (Clojure team)23:05:15

Back in old contrib, Stuart Sierra had some stuff for this


I see the code in which creates StaticMethodExpr, InstanceMethodExpr, StaticFieldExpr, or InstanceFieldExpr. Where does the rewriting happen to the special form .. Is that in the reader?

Ivan Koz23:05:58

@dpsutton in HostExpr parser i guess


that's what i was seeing already but it seems to already be in (. Integer toHexString) form there

Ivan Koz23:05:15

at compile time, when bytecode is being generated, so after reading but before evaluation i guess

Alex Miller (Clojure team)23:05:37

it's read by the reader as a list invoking a symbol

Alex Miller (Clojure team)23:05:49

the compiler is the one that handles it


thanks @alexmiller. super helpful and informative as always

👍 4
Alex Miller (Clojure team)23:05:41

technically, I think it's in the analyzer part of the compiler where it decides what kind of symbol it is, like in analyzeSymbol

Alex Miller (Clojure team)23:05:58

that figures out if it's a var, static call, class, etc


Is there an idiomatic/built in way to perform an instance? with multiple possible classes? In this case I am checking if an exception I have been handed is one of several different possible classes

Alex Miller (Clojure team)23:05:47

nothing but the obvious

Alex Miller (Clojure team)23:05:20

Java has syntax for that now, but we haven't done anything in Clojure to handle it

Alex Miller (Clojure team)23:05:32

typically you'd make multiple catch blocks for each option

Alex Miller (Clojure team)23:05:53

maybe a possible future extension to echo Java there


In this case it's outside of a catch (dealing with exceptions coming in from a clj-http.client async call)


@robertfrederickwarner supers returns a set, and you can easily check if various classes are in it


eg. check set/intersection to do multiple matches in one direction, set/contains in the other direction

Alex Miller (Clojure team)01:05:07

have to be careful with that stuff though - classes are not constants, they are classloader dependent


Is there any way to find out what namespace is requiring the current one? My use case is I have a namespace (a) that registers a bunch of multimethod handlers that come from namespace (b), but doesn't really export anything. I want to make sure those handlers get registered whatever the calling context, so I'm re-exporting the multimethod from (a). I'd like to enforce the convention that that multimethod is only imported from (a), not from (b). If there's a better way to do this, feel free to suggest it 🙂


create a service provider interface namespace, but your defmethods in there, create implementation namespaces, and if needed create a namespace that pulls in both spi and implementations and defines functions that call the spi


creates a diamond structure