Fork me on GitHub
Jim Newton07:04:56

I'm using java.lang.constant.Constable in some of my test cases of my project. This runs find in the interactive session using cider. However, it fails in the gitlab CI pipeline. The pipeline effectively has the following expression:

(and (symbol tag) (resolve tag) (class? (resolve tag)))
which gets evaluated during the testing with tag =the symbol java.lang.constant.Constable

Jim Newton07:04:44

What do I need to do to make sure that symbol resolves using the `resolve` function?

Jim Newton07:04:43

when I run lein test it works. all the tests pass, but when I run this in the docker image it fails. i.e. that expression (and (symbol tag) (resolve tag) (class? (resolve tag))) returns false.

Jim Newton07:04:58

do I need to add something to my ns :require clause to make sure java.lang.constant.Constable is available ?


What JDK version are you running on the system where it works, versus the one where it does not? I do not know when that class was added to the JDK, but it might not be until JDK version 12.

Jim Newton08:04:29

[geminiani:~/Repos/scalain_e] jimka% java --version
java 14 2020-03-17
Java(TM) SE Runtime Environment (build 14+36-1461)
Java HotSpot(TM) 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)
[geminiani:~/Repos/scalain_e] jimka% 


You can find out from inside of a running Clojure program with (System/getProperty "java.version"))

Jim Newton08:04:27

so it sounds like I need to find other classes which serve the same purpose for testing but are available pre-jdk 12

Jim Newton08:04:05

yea, it returns "14" as a string


It seems to at least partly depend upon what JDK versions you want your code to work on. JDK 8 is still fairly widely used, and the other most common one is likely JDK 11

Jim Newton08:04:20

great. i refactored my tests and just pulled the java-14 specific tests into different test cases which are "optioned out" if (resolve 'java.lang.constant.Constable) returns false

Jim Newton08:04:15

tests pass in the docker image as well. happy camper here.

Jim Newton07:04:26

What do I need to do to make sure that symbol resolves using the resolve function?


Is there an inverse of map destructuring f where (f [x y]) => {:x x :y y}?


I remember seeing a variant of let that returns all bindings in a map. Can't seem to find it at the moment.


But I don't thing it's worth it TBH. If there are so many bindings you don't want to write them as a map, you're better off propagating them as a map through all the levels. Is there a particular snippet that sparks the desire for something like this?


For an inline function call, I don't need to spread the call over multiple lines if the caller code is succinct.


But it's a minor thing.

Jim Newton10:04:01

If I have a function with a keyword argument, how can I make its default value to be false

(defn dfa-to-dot 
  [dfa & {:keys [title view abbrev]
          :or {title "no-title"
               abbrev true
               view false}}]
If I make the default value of abbrev true then I can call the function with :abbrev true or :abbrev false, however if I make the default false, then calling it with :abbrev true or :abbrev false seem to work exactly the same.

Jim Newton10:04:56

For this function, what can i do to make the default value be true and have :abbrev false still work? The following unfortunately returns true, even though I passed false at the call site.

((fn [& {:keys [abbrev] :or {abbrev true}}] abbrev) :abbrev false)


no, it doesn't?

user=> ((fn [& {:keys [abbrev] :or {abbrev true}}] abbrev) :abbrev false)

Jim Newton10:04:40

hey! your'e right. there must be something else wrong my code. I've never had a bug before 🙂

Jim Newton10:04:00

My problem was that my function made a recrusive call to itself, and didn't pass along the :abbrev variable in the call. 😞


is there any marco for debug clojure code?? too much error has been shown


hi everyone, I have a question about an atom inside another atom

    (def parent-atom (atom {}))
    (swap! parent-atom merge {:a (atom 1)})
    (def child-atom (:a @parent-atom))
    (reset! parent-atom {})
    (prn (deref child-atom)))
Why (deref child-atom) returns 1 ?


because you set it to 1 !


yes, but I reset! the parent ones, shouldn't it return nil?


Is there another programming language you know? JS, Java?


Java and JS


I can show you by typing it in another language.


k, one sec


var parent = {}
var child;

parent = {a: {val: 1}}
child = parent.a
parent = {}
// -> 1


make more sense now?


so, when the child is created, it creates a new value from the value of the parent, not a reference to that value ?


thanks, finally got it.

✔️ 4

No. You have a reference to an object. Resetting parent does not remove your reference to that object.


To be clear: You should not be putting atoms within atoms.


It’s fine for exploration, but for real code, that’s a great way to make a mess.

Chris K12:04:34

What does the :pre for the function arguments do? I think it is for limiting the viable arguments of the function but just wanted to get it verified


it allows design by contract, you can use :pre and :post to check the input values and output values that matches a predicate

Chris K12:04:33

nice I don't think I've used a language that lets u check the value for the output before

Chris K12:04:09

Some example stuff:

{:pre [(pos? x)]}


It causes the compiler to insert an assertion. It’s equivalent to:

(assert (and (pos? x) ...))

Chris K12:04:36

oh ok, so to conclude it's for filtering the arguments? I could say?


:pre assertions get run before your code. :post assertions get run on your return value.


No not filtering.


It throws exception if arguments are invalid.

Chris K12:04:31

just like limiting basically


Limits the cases you have to consider in the function, yes.

Chris K12:04:56

alright thxs 😄


However: You can turn these off when you invoke the JVM.


(as with all JVM assertions)

Chris K12:04:19

got it thxs

Ramon Rios14:04:33

Hello everyone, if i want to add a optional argument on my function i need to use a & right? I'm using it and getting a error when i do not fill this function.

Ramon Rios14:04:33

for example

(create-table-sql [this table cols])
I want to add a optional argument
(create-table-sql [this table cols & extra-specs])


Try this approach:

(defn create-table-sql
  ([this table cols] (create-table-sql this table cols [])
  ([this table cols extra]
    (do-something-with-args ,,,)))


That works well if you know you’re going to have at-most-one extra argument, and the & version works well when you’re going to have 0 .. n additional arguments


Also from your example, it’s not clear if you’re using the & in the defn or if you’re using it when you call the function. You need to use the & in the defn that defines your function.

Ramon Rios14:04:50

On my case, i'll have more than one argument.

Ramon Rios14:04:03

I'm using it in the defn.


Can you post a gist of your code and the error you’re getting?

Ramon Rios14:04:14

java.lang.IllegalArgumentException: No single method: create_table_sql of interface: dn.migrate.ddl.Ddl 
found for function: create-table-sql of protocol: Ddl


Ok, protocols are adding some extra complexity here then. I’m not that well versed in protocols, so I should let someone else give you a more reliable answer.

Alex Miller (Clojure team)14:04:02

protocols don't support varargs


That would explain it. 🙂

Alex Miller (Clojure team)14:04:33

you have to define the method multiple times with different arities

Alex Miller (Clojure team)14:04:01

protocols internally are making java interfaces and that's drives some of this


So if you need 0 .. n optional arguments, you can just wrap them inside a vector as a workaround, right?


(create-table-sql this table cols [some more extra stuff])

Alex Miller (Clojure team)14:04:51

you could, although you'd still have to pass that always

Ramon Rios14:04:32

Thank you folks

Ramon Rios14:04:39

i'm figure out another way

Ramon Rios16:04:56

I create another arity trying to call the same implementation. I appreciate the help

Phil Hunt16:04:42

Are protocols what Clojure has instead of CLOS kinda? Some of the concepts seem related.


I do not know CLOS in depth enough to compare them in details, but Clojure also has quite general multimethods: Nothing in Clojure I am aware of tries to achieve what CLOS's MOP (Meta Object Protocol) does. I would not be surprised at all if CLOS enables solving "the expression problem" in a similar way that Clojure protocols are intended to do:

Phil Hunt16:04:52

Oh that looks like an interesting read. Thanks. (Thanks also for the good resources I recognise your name from Andy)

Phil Hunt16:04:46

Yes, it was the function-centric polymorphism/multimethod stuff that struck me as similar, I have yet to comprehend MOP 🙂


there's a similarity / compromise between what defmulti does and what clos does for multimethods


in clojure you could absolutely use (fn [& args] (map type args)) as your method dispatch to do what clos does


but instead of MOP for fully parameterized object system where you can redefine inheritance, method lookup, etc. you just get a per method option for a custom dispatch


(I wonder if that's fully compatible though - would defmulti be able to use each element of (map type args) to look for supers - that might not actually be possible)


so I guess you can kind of fake it, but without subclassing, which kind of misses the point, so I take it back - it's only vaguely similar if you squint, and not really the same


this reminds me of the docs for cut in scheme claiming that it implements currying :D


hey guys, i’m trying to read in a CSV file that has a header and then ultimately end up with a list/vector of maps containing keywords and the correct type


i’ve tried several csv libraries, but have a hard time finding one i can make ‘work’


hardest to translate is a datetime type


how would you go about doing this?

Alex Miller (Clojure team)17:04:41

I use org.clojure/data.csv for handling lots of csv fiels


i got furthest with clojure/data.csv and ultra-csv


i tried that one first yes

Alex Miller (Clojure team)17:04:11

you're often going to need some glue for stuff like date/time


and was able to get 80% of the way


I believe with data.csv you always get strings for field values, and it is up to you to convert those to the type that you wish.


yeah i got that, for example:

Alex Miller (Clojure team)17:04:48

the java.time libraries can do anything kind of date/time parsing you need


(defn str->keyword-concat-and-lowercase 
  "turn a string into lower cased concat keyword"
  (as-> (name raw_string) $
    (string/replace $ " " "_")
    (string/lower-case $)
    (keyword $)))


turns it into lowercase concat keywords


however then the translation of strings to the correct type


what i did now with data.csv is this:


;; (defn convert-various-types
;;   "Convert Balance, Amount, Currency Rate to number
;;    Convert Date to date"
;;   [csv-record]
;;   (-> csv-record
;;       (update :amount #(bigdec %))
;;       (update :balance #(bigdec %))
;;       (update :currency_rate #(bigdec %))
;;       (update :date #(clojure.instant/read-instant-date %))))
;;     ;;   (update :date #(.parse (java.text.SimpleDateFormat. "MM/dd/yyyy" %)))))


mind my beginner’ness


i couldn’t get the date/time to work


however also frustratingly i thought there would be a better way than 4 updates to convert the types


Google showed me no useful results

Alex Miller (Clojure team)17:04:50

I would probably merge into the record for something like this

Alex Miller (Clojure team)17:04:09

(merge csv-record {:amount ... , :balance ... })

Alex Miller (Clojure team)17:04:28

I think you've got the args slightly off on the date thing


thanks, i will look into merge


yeah, i don’t know how to do dates

Alex Miller (Clojure team)17:04:47

(update :date #(.parse (java.text.SimpleDateFormat. "MM/dd/yyyy" %)))
(update :date #(.parse (java.text.SimpleDateFormat. "MM/dd/yyyy") %))

🙂 4
Alex Miller (Clojure team)17:04:28

that should work, your % was just an arg in the wrong place

Phil Hunt18:04:16

That's timely, I was trying to figure out how to do stuff with the Java date libs


how would you go about the merge?


i would create a new map with the same keywords and the correct types?

Alex Miller (Clojure team)17:04:42

merge just merges N maps, last one wins

Alex Miller (Clojure team)17:04:58

then the overridden keys will replace


so i have 1 data set of 5566 maps


and then 1 data set consisting of just 1 map


then merge them and ensure the correct types are that 1 map and are put last in the (merge )

Alex Miller (Clojure team)18:04:16

I'm just talking about in the function above which is just on a single record

Alex Miller (Clojure team)18:04:30

(defn convert-various-types
  "Convert Balance, Amount, Currency Rate to number
   Convert Date to date"
  [{:keys [amount balance currency_rate date] :as csv-record}]
  (merge csv-record
    {:amount (bigdec amount)
     :balance (bigdec balance)
     :currency_rate (bigdec currency_rate)
     :date (.parse (java.text.SimpleDateFormat. "MM/dd/yyyy") date)}))

👍 4

the % movement did the trick for date time conversion 🙂


thinking out loud - what about an approach like (reduce-kv (fn [m k translation] (update m k translation)) input {:amount bigdec :balance bigdec ... ...})


and handy enough, that just translates to (reduce-kv update input {:amount bigdec :balance bigdec ... ...}) since update already has the right arg list

Alex Miller (Clojure team)18:04:45

that looks significantly harder to read/maintain to me, but I'm dumb :)


thank you Alex, this all worked like a charm!


oh, yeah - thats' why I split it in two, but it's still weird


i’m actually learning about Destructuring

Alex Miller (Clojure team)18:04:27

yes, you can make some sort of general transformation framework out of stuff like this

Alex Miller (Clojure team)18:04:44

in general, I find the result is rarely worth the effort

Alex Miller (Clojure team)18:04:57

esp if you're doing it once


quick question about the destructured solution as this would have never come to my mind


why are we destructuring here?


i understand the :as to get the full map as well


am i correct to say that it would be much more code on each line to get the right key from csv-record


than doing it this way?


as in, {:amount (bigdec amount) would be {:amount (bigdec (:amount csv-record)) ?


Yeah, that's right.


great 🙂 learned something today


thanks guys for your help!

Alex Miller (Clojure team)18:04:56

really, a large percentage of clojure functions look like this :)


is there a fn like doseq or for that short circuits evaluating the rest of the sequence if any body expr throws an error?


as in throws an exception? doseq will definitely stop if you throw an exception


I wonder if you could make for stop at an excption though (in terms of the sequence returned)


yep, it seems like I was using ex-info incorrectly within a doseq block. an ex-info call without a cause argument wasn’t causing a breakout of the doseq for some weird reason


ex-info is just a constructor, it doesn't throw anything


user=> (type (ex-info "foo" {:a 0}))


yep, that explains it! seems like if there’s a cause added to the constructor it’ll get caught by a catch block without calling throw , otherwise not so much


there's a potential for confusion, because the way an ex-info object prints in a repl


no, that is not how anything works


you need to call throw, (or else maybe create the ex-info incorrectly, so that the attempt to create it throws... - which works accidentally)


even if you provide the cause arg, it's still just an object, which doseq would silently ignore

user=> (type (ex-info "foo" {:a 0} (Exception. "bar")))


yes you’re correct. I understand. I was looking at something in the repl from a previous execution


appreciate the clarification

Jason M Jones20:04:26

Hello everyone. I am fairly new to Clojure. I've been learning about Clojure since I read this article a little less than a year ago: I was fascinated, and as I have studied have loved how Clojure works. I'm eager to be here and to learn more.

👋 20
🙂 8

Hi. When I start the repl with the 'clojure' command in Macos terminal, I am getting weird characters when pressing the arrow keys ^[[A etc. Is there a way to fix this ?


clojure isn't meant for interactive usage, use clj which has readline (clj and clojure are both installed with the setup)


those "weird characters" are what arrow keys actually emit, and they will come through when you use programs that are not meant for interactive input


clj works, thanks!