Fork me on GitHub
#beginners
<
2020-04-28
>
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 ?

andy.fingerhut07:04:35

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% 

andy.fingerhut08:04:23

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

andy.fingerhut08:04:21

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?

hindol09:04:38

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

jaihindhreddy09:04:41

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

jaihindhreddy10:04:44

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?

hindol10:04:48

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

hindol10:04:38

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 
   "docstring"
  [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)

jsn10:04:00

no, it doesn't?

user=> ((fn [& {:keys [abbrev] :or {abbrev true}}] abbrev) :abbrev false)
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. 😞

Steiner12:04:16

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

Ludwig12:04:22

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

(do
    (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 ?

potetm12:04:53

because you set it to 1 !

Ludwig12:04:13

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

potetm12:04:00

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

Ludwig12:04:10

Java and JS

potetm12:04:11

I can show you by typing it in another language.

potetm12:04:15

k, one sec

potetm12:04:07

var parent = {}
var child;

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

potetm12:04:35

make more sense now?

Ludwig12:04:52

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

Ludwig12:04:22

thanks, finally got it.

✔️ 4
potetm13:04:08

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

potetm12:04:35

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

potetm12:04:59

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

Ludwig12:04:40

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)]}

potetm12:04:27

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?

potetm12:04:48

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

potetm12:04:58

No not filtering.

potetm12:04:12

It throws exception if arguments are invalid.

Chris K12:04:31

just like limiting basically

potetm12:04:46

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

Chris K12:04:56

alright thxs 😄

potetm12:04:04

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

potetm12:04:10

(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])

manutter5114:04:51

Try this approach:

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

manutter5114:04:28

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

manutter5114:04:40

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.

manutter5114:04:34

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

manutter5114:04:01

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

manutter5114:04:29

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

manutter5114:04:18

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

manutter5114:04:39

(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.

andy.fingerhut16:04:11

I do not know CLOS in depth enough to compare them in details, but Clojure also has quite general multimethods: https://clojure.org/reference/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: https://www.ibm.com/developerworks/library/j-clojure-protocols/

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 🙂

noisesmith17:04:34

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

noisesmith17:04:20

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

noisesmith17:04:11

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

noisesmith17:04:35

(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)

noisesmith17:04:20

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

noisesmith17:04:15

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

naxels17:04:48

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

naxels17:04:08

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

naxels17:04:20

hardest to translate is a datetime type

naxels17:04:26

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

naxels17:04:00

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

naxels17:04:09

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

naxels17:04:17

and was able to get 80% of the way

andy.fingerhut17:04:29

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.

naxels17:04:43

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

naxels17:04:50

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

naxels17:04:08

turns it into lowercase concat keywords

naxels17:04:21

however then the translation of strings to the correct type

naxels17:04:31

what i did now with data.csv is this:

naxels17:04:54

;; (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" %)))))

naxels17:04:58

mind my beginner’ness

naxels17:04:10

i couldn’t get the date/time to work

naxels17:04:30

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

naxels17:04:39

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

naxels17:04:42

thanks, i will look into merge

naxels17:04:47

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

naxels17:04:24

how would you go about the merge?

naxels17:04:39

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

naxels17:04:30

so i have 1 data set of 5566 maps

naxels17:04:39

and then 1 data set consisting of just 1 map

naxels17:04:58

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
naxels18:04:32

the % movement did the trick for date time conversion 🙂

noisesmith18:04:56

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

noisesmith18:04:37

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 :)

naxels18:04:56

thank you Alex, this all worked like a charm!

noisesmith18:04:05

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

naxels18:04:13

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

naxels18:04:36

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

naxels18:04:43

why are we destructuring here?

naxels18:04:07

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

naxels18:04:47

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

naxels18:04:50

than doing it this way?

naxels18:04:48

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

jaihindhreddy18:04:07

Yeah, that's right.

naxels18:04:22

great 🙂 learned something today

naxels18:04:26

thanks guys for your help!

Alex Miller (Clojure team)18:04:56

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

mss19:04:26

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

bfabry19:04:49

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

noisesmith19:04:16

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

mss19:04:54

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

noisesmith19:04:41

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

noisesmith19:04:12

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

mss19:04:51

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

noisesmith19:04:59

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

noisesmith19:04:08

no, that is not how anything works

noisesmith19:04:31

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

noisesmith19:04:08

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")))
clojure.lang.ExceptionInfo

mss19:04:57

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

mss19:04:00

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: http://blog.cleancoder.com/uncle-bob/2019/08/22/WhyClojure.html. 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
🙂 8
dabrazhe21:04:54

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 ?

noisesmith21:04:01

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

noisesmith21:04:42

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

dabrazhe21:04:34

clj works, thanks!