Fork me on GitHub
#beginners
<
2020-04-28
>
jimka.issy07: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

jimka.issy07:04:44

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

jimka.issy07: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.

jimka.issy07: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.

jimka.issy08: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"))

jimka.issy08: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

jimka.issy08: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

jimka.issy08: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

jimka.issy08:04:15

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

jimka.issy07:04:26

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

hindol.adhya09: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?

hindol.adhya10:04:48

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

hindol.adhya10:04:38

But it's a minor thing.

jimka.issy10: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.

jimka.issy10: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)

jason35810:04:00

no, it doesn't?

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

jimka.issy10:04:40

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

jimka.issy10: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. 😞

steiner304412:04:16

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

vachichng12: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 !

vachichng12: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?

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?

vachichng12: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 ?

vachichng12:04:22

thanks, finally got it.

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.

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

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

sunchaesk12:04:33

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

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

sunchaesk12: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.

sunchaesk12:04:31

just like limiting basically

potetm12:04:46

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

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

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

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

ramonp.rios14:04:50

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

ramonp.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?

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

alexmiller14:04:02

protocols don't support varargs

manutter5114:04:29

That would explain it. 🙂

alexmiller14:04:33

you have to define the method multiple times with different arities

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

alexmiller14:04:51

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

ramonp.rios14:04:32

Thank you folks

ramonp.rios14:04:39

i'm figure out another way

ramonp.rios16:04:56

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

phil63416: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/

phil63416:04:52

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

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

patrick.glind17: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

patrick.glind17:04:08

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

patrick.glind17:04:20

hardest to translate is a datetime type

patrick.glind17:04:26

how would you go about doing this?

alexmiller17:04:41

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

patrick.glind17:04:00

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

patrick.glind17:04:09

i tried that one first yes

alexmiller17:04:11

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

patrick.glind17: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.

patrick.glind17:04:43

yeah i got that, for example:

alexmiller17:04:48

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

patrick.glind17: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 $)))

patrick.glind17:04:08

turns it into lowercase concat keywords

patrick.glind17:04:21

however then the translation of strings to the correct type

patrick.glind17:04:31

what i did now with data.csv is this:

patrick.glind17: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" %)))))

patrick.glind17:04:58

mind my beginner’ness

patrick.glind17:04:10

i couldn’t get the date/time to work

patrick.glind17:04:30

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

patrick.glind17:04:39

Google showed me no useful results

alexmiller17:04:50

I would probably merge into the record for something like this

alexmiller17:04:09

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

alexmiller17:04:28

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

patrick.glind17:04:42

thanks, i will look into merge

patrick.glind17:04:47

yeah, i don’t know how to do dates

alexmiller17:04:47

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

alexmiller17:04:28

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

phil63418:04:16

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

patrick.glind17:04:24

how would you go about the merge?

patrick.glind17:04:39

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

alexmiller17:04:42

merge just merges N maps, last one wins

alexmiller17:04:58

then the overridden keys will replace

patrick.glind17:04:30

so i have 1 data set of 5566 maps

patrick.glind17:04:39

and then 1 data set consisting of just 1 map

patrick.glind17:04:58

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

alexmiller18:04:16

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

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

patrick.glind18: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

alexmiller18:04:45

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

patrick.glind18: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

patrick.glind18:04:13

i’m actually learning about Destructuring

alexmiller18:04:27

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

alexmiller18:04:44

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

alexmiller18:04:57

esp if you're doing it once

patrick.glind18:04:36

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

patrick.glind18:04:43

why are we destructuring here?

patrick.glind18:04:07

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

patrick.glind18: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

patrick.glind18:04:50

than doing it this way?

patrick.glind18:04:48

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

jaihindhreddy18:04:07

Yeah, that's right.

patrick.glind18:04:22

great 🙂 learned something today

patrick.glind18:04:26

thanks guys for your help!

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

joneski20: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.

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

dennisa21:04:34

clj works, thanks!