Fork me on GitHub
#beginners
<
2021-02-09
>
Yang Xu02:02:56

Hi, How to get the key of Enum instead of the value when converting Java object to Clojure map using bean or javadata/from-java?

andy.fingerhut03:02:44

I don't think I understand your question. If I call bean on a Java object, it returns a value that acts like a Clojure map, and its keys are all keywords, e.g.:

user=> (def m1 (bean (atom 5)))
#'user/m1
user=> m1
{:class clojure.lang.Atom, :validator nil, :watches {}}
user=> (keys m1)
(:class :validator :watches)
user=> (map class (keys m1))
(clojure.lang.Keyword clojure.lang.Keyword clojure.lang.Keyword)

andy.fingerhut03:02:22

All of the keys are Clojure keywords. You want a map where the keys are some other non-keyword values?

Yang Xu03:02:29

Sorry, I don't describe it clearly. The problem is I have an Enum in Java like this:

public enum Test {
    A("a", ""),
    B("b", "")
}
So, I want to get the key of Enum when converting Java objects to Clojure maps. Now is get the name of Enum.

andy.fingerhut03:02:42

Can you give an example of something you have tried in a REPL with such an enum, showing the return value that you get (but wish it were different), and what return value you wish to get? I still do not understand what you are after.

Yang Xu04:02:18

public class Test {
    private QueryTypeEnum type;

    public enum QueryTypeEnum {
          A("a", ""),
          B("b", "");
          private String key;
          private String desc;
    }
}

(bean test instance) or (from-java test instance) 
My except map is like this: {":type" "a"}, But it will take the name of Enum, so the result will be like this: {":type" "A"}.
Now, I found a way to solve it. I  rewrite defmethod from-java [Enum]  inside the java.data like this:
(defmethod j/from-java Enum [enum] (.getKey enum))

popeye05:02:42

Team , I have a vector like below

[A1 B1 C1  
  A2 B2 C2 
 A3 B3 C3]

popeye05:02:06

I wanted to split and put it like

[[A1 B1 C1 ][A2 B2 C2 ][A3 B3 C3]]

popeye05:02:36

so that I can iterate over 3 elements each and pass as argument

popeye05:02:43

how can I achieve in clojure

emilaasa06:02:58

(mapv vec (partition 3 [:A1 :B1 :C1 :A2 :B2 :C2 :A3 :B3 :C3]))

emilaasa06:02:37

PS I'm not very experienced in clj either so take it with a grain of salt 🙂

popeye06:02:40

@rextruong what if input is like this

["A1 B1 C1"
  "A2 B2 C2" 
 "A3 B3 C3"]

popeye06:02:35

output should be

[["A1" "B1" "C1" ]["A2" "B2" "C2" ]["A3" "B3" "C3"]]

seancorfield06:02:12

@popeyepwr (map #(clojure.string/split % #" ") input) will be close to what you want...

seancorfield06:02:05

user=> (def input ["A1 B1 C1"
                   "A2 B2 C2" 
                   "A3 B3 C3"])
#'user/input
user=> (map #(clojure.string/split % #" ") input)
(["A1" "B1" "C1"] ["A2" "B2" "C2"] ["A3" "B3" "C3"])
user=> 

3
👍 6
Adrian Imanuel07:02:05

ahhh... i forgot that clojure.string/split functions! gonna take note again.

seancorfield06:02:01

mapv if you really want a vector back instead of a (lazy) sequence.

aratare06:02:35

Ah sorry I thought they are all separate strings.

emilaasa06:02:59

Do you want to turn a vector of strings into a vector of vectors of strings? (mapv vector ["A1 B1 C1" "A2 B2 C2" "A3 B3 C3"]) ; [["A1 B1 C1"] ["A2 B2 C2"] ["A3 B3 C3"]]

dpsutton07:02:10

your solution yields a vector who's elements are single item vectors. ["A1 B1 C1"]. The desired shape is a vector of 3 item vectors [["A1" "B1" C1"] ...]

dpsutton07:02:01

yeah it's hard to see on the slack client

emilaasa07:02:13

Not seeing quotes, probably the biggest time sink in my life 😄

dpsutton07:02:15

i had to do a bit of a double take myself

emilaasa07:02:29

Interesting how the brain works, I looked at the question from one hour ago and then I did not even see the quotes in the revised question

emilaasa06:02:11

or am I misunderstanding something 🙂

mbjarland09:02:51

I assume that if I create a channel with a distinct transducer, eg

(async/chan 10 (distinct))
that this will not be thread safe, i.e. it is not safe for multiple threads to read/write from the channel?

mbjarland09:02:08

or even just the distinct transducer without the channel

mbjarland09:02:33

if I wanted a thread safe distinct…what would you choose? locking ?

hiredman09:02:47

It is a bad idea because distinct will hold a copy of every message that passes through the channel in a set

mbjarland09:02:27

@hiredman thank you. This is a short lived tree traversal, multi threaded yes, but accumulation is not an issue. Hmm…I read about the volatile used in distinct and came out thinking it would not be safe. Care to elaborate a bit?

hiredman09:02:42

The main thing is every channel has a lock inside it, and when you add or remove things from the channel (which is when the transducer operates) you hold the lock

mbjarland09:02:13

ok got it, so distinct by itself outside a channel is not thread safe, but used as a transducer for a channel we get safety

hiredman09:02:55

The usage of volatile inside a lot of the mutable transducers is there to make them safe for use with core.async

hiredman09:02:55

The volatile makes sure mutations propagate between threads, the channel lock says one thread at a time

mbjarland09:02:01

makes sense. Thanks again!

Adam Helins09:02:15

Damn, I realized I didn't know how to pass a java property using -J (Clojure CLI). I tried many ways of prefixing it without any luck, an example in the reference guide would be welcome

hiredman09:02:07

A property would -J-Dfoo=bar

Adam Helins10:02:00

All right, it was about the order of args... -J must figure before -M. Kinda makes sense but my coffee wasn't strong enough this morning...

Yang Xu12:02:03

Hi, I using Java class to representing Clojure dataset like this:

{
  "query": {
    "source-table": 2,
    "aggregation": [
      [
        "distinct",
        [
          "field-id",
          5
        ]
      ],
      [
        "sum",
        [
          "field-id",
          8
        ]
      ]
  }
}
Now, I have classes here:
public class QueryReq {
    private Query query;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Query {
        private String sourceTable;
        private List<Aggregation> aggregation;
    }


    @Data
    public class Aggregation {
        private AggregationEnum aggregation;
        private AggregationColumn aggregationColumn;

        @Data
        public class AggregationColumn {
            private KeywordEnum column;
            private Integer id;
        }
    }
So, my question is how to convert AggregationColumn which nested in the Aggregation to Clojure vector? Is here have some convenient ways to do converting datastructure between Java and Clojure?

andy.fingerhut16:02:08

Java classes pretty much all have custom APIs defined by their methods, so I cannot imagine any general technique to convert between Java object values and Clojure values. If you use Java classes that implement interfaces like List, Map, and Set, Clojure collections implement those interfaces, so at least in some cases no conversion is necesary -- they already satisfy those interfaces.

Eamonn Sullivan13:02:09

I'm having some trouble with a reflection warning. I have a library, with a defrecord in it that extends a defprotocol. E.g.. (defrecord SomeThing [name type] myProtocol ...). This protocol has a release-props method. When I make a call to this method, like so:

(.release-props my-thing)
I'm getting a reflection warning call to method release_props can't be resolved (target class is unknown) . I'm trying to resolve it like (.release-props ^mylib/SomeThing my-thing), but that's not working. I get Unable to resolve classname. What am I doing wrong?

Eamonn Sullivan14:02:25

Maybe I'm missing something on the library side? I admit to having no clue about what (:gen-class) does...

Eamonn Sullivan14:02:51

even a fully qualified name like ^my.lib.SomeThing doesn't work. I still get a ClassNotFoundException.

Eamonn Sullivan14:02:04

I guess the most basic question is: how do I refer to a record in a library? I just get No such var

Alex Miller (Clojure team)14:02:03

Java method names can’t have a - in them

Eamonn Sullivan14:02:50

Doh! Showing my Java ignorance again.

Alex Miller (Clojure team)14:02:57

So the . call with the - doesn’t seem right

Eamonn Sullivan14:02:14

It works, though. Just gets a reflection warning.

Alex Miller (Clojure team)14:02:41

Yeah, it might be getting munged

Eamonn Sullivan14:02:10

Ah, right. I'll give these methods more JavaLikeNames, then. Thank you!

Alex Miller (Clojure team)14:02:46

It’s just that calling via java interop requires java naming

Eamonn Sullivan14:02:19

Excuse the extreme ignorance, but is there another way to call a protocol method here?

Alex Miller (Clojure team)14:02:27

If you have a protocol, why are you not invoking the protocol method?

Alex Miller (Clojure team)14:02:07

(release-prop my-thing)

Eamonn Sullivan14:02:39

Because this is the very first time I have used protocols and thought that was how they work.

Alex Miller (Clojure team)14:02:03

Ok, they’re just like regular functions when you invoke them

Alex Miller (Clojure team)14:02:39

And protocol functions exist in the namespace where they’re declared so you require/refer them just like any other function

Eamonn Sullivan14:02:07

Bingo, that did it. Thank you!

Yang Xu14:02:06

Anyone know that does java-data/from-java can work with the Java Inner class?

andy.fingerhut15:02:41

I have not tried it before, but do not know of any reason why it shouldn't work, either. Inner classes named from Clojure have names like OuterClass$InnerClass -- did you try naming the inner class that way?

andy.fingerhut15:02:55

If so, what did you try, and what unexpected result did you get?

Yang Xu03:02:59

The example code is the same as the previous question, I defined Inner class defmethod like this:

(defmethod j/from-java QueryReq$Aggregation [agg]
  (println "ac:"))
But "ac:" don't print, so that makes me feel that my defined function don't be called.

Michaël Salihi14:02:27

Hi everybody! From this map:

(def homes [{:name "Home 1",
             :wifi "y",
             :shower "y",
             :laundry "y",
             :metro "n",
             :balcony "y",
             :fireplace "n",
             :pool "y"},
            {:name "Home 2",
             :wifi "n",
             :shower "y",
             :laundry "y",
             :metro "n",
             :balcony "n",
             :fireplace "n",
             :pool "n"}
            {:name "Home 3",
             :wifi "y",
             :shower "y",
             :laundry "y",
             :metro "n",
             :balcony "y",
             :fireplace "y",
             :pool "n"}
            {:name "Home 4",
             :wifi "y",
             :shower "y",
             :laundry "n",
             :metro "n",
             :balcony "n",
             :fireplace "n",
             :pool "n"}]);
I want to only keep/filter houses meeting the following needs (def needs [:wifi :shower :laundry]) Is my solution a decent one? (filter #(every? #{"y"} (vals (select-keys % needs))) homes) Did you have suggestions?
(filter #(every? #{"y"} (vals (select-keys % needs))) homes)

({:name "Home 1",
  :wifi "y",
  :shower "y",
  :laundry "y",
  :metro "n",
  :balcony "y",
  :fireplace "n",
  :pool "y"}
 {:name "Home 3",
  :wifi "y",
  :shower "y",
  :laundry "y",
  :metro "n",
  :balcony "y",
  :fireplace "y",
  :pool "n"})

Michael Stokley15:02:01

i might consider modeling like this:

(def homes' [{:name "Home 1",
                :amenities #{:wifi :shower :laundry :pool}},
               {:name "Home 2",
                :amenities #{:shower :laundry}}
               {:name "Home 3",
                :amenities #{:wifi :shower :laundry :balcony :fireplace}}
               {:name "Home 4",
                :amenities #{:wifi :shower}}])

Michael Stokley15:02:09

(def needs' #{:wifi :shower :laundry})

Michael Stokley15:02:31

then to filter:

(filter
   (fn [home] (clojure.set/subset? needs' (:amenities home) ))
   homes')
;; => ({:name "Home 1", :amenities #{:pool :shower :laundry :wifi}}
;;     {:name "Home 3", :amenities #{:shower :laundry :balcony :wifi :fireplace}})

👍 6
Michaël Salihi15:02:21

Thx for your suggestion. Concerning the modeling, let's assume that it can be changed because it comes from a converted JSON.

👍 3
herald15:02:06

I think your solution is good, although you could simplify it by changing (vals (select-keys % needs)) to (map % needs).

👌 3
Michaël Salihi16:02:44

Perfect @UCW9TUDNK! I suspected that there was a way to simplify. Thank you, that was exactly the advice sought. 👍

Michaël Salihi16:02:30

Maps as functions ... it's really powerful!

herald16:02:15

Yes, and it doesn't just stop at maps [=

Michaël Salihi16:02:55

Yes, sets as function too 🙂

johnjelinek15:02:43

is there any possibility to do java interop from source files instead of compiling first? Or is there a way to use deps to build local java libs first before starting the repl?

Alex Miller (Clojure team)15:02:11

no to the first question. the latter is possible but probably depends on lots of things

johnjelinek17:02:25

are there any reference apps or starter kits that include this workflow for reference?

Alex Miller (Clojure team)17:02:38

are these local java libs things that are yours or external libs? the latter is nearly always built and published to a maven repo somewhere and you should just include the lib that way.

Alex Miller (Clojure team)17:02:46

are you looking at java-only or java/clojure mix?

johnjelinek17:02:24

so, my use-case is running the terraform CDK (`cdktf`). It ships with a java flavor (but also typescript, python, and dotnet-core as well), and when you add provider dependencies (ie: docker), you run cdktf get and it pulls down some typescript and generates java source to be consumed by your project. For a java project, it then is accessible by the code you've written yourself, but for clojure -- it's not yet compiled. Usually cdktf get code is not checked into the repo, it goes in a .gen directory.

johnjelinek17:02:56

but my use-case is to only write clojure with java interop

phronmophobic18:02:40

if you don't need to recompile very often, I was able to use this one liner to compile java files:

find javasrc -name '*.java' -print0 | xargs -0 javac -d classes -cp `clj -Spath
` make sure the directory classes exists and add it to the paths in your deps.edn which should have a key like:
:paths ["src" "resources" "classes"]

johnjelinek19:02:16

OH! That's interesting

Joel Whittier15:02:04

Hey, I’m coming from Haskell and excited about clojure! I’m working through 4clojure and some basic problems right now just getting the hang of it. Are there any recommended intro projects out there? Would like to dig into something but I’m not sure what’s reasonable in this language.

Eric Le Goff15:02:24

While I can solve easy 4clojure puzzles, I am curious about the best learning strategy when stuck. Obviously I can use a search engine for some public gist where other users would share their solution and revert engineer it, but is there a more obvious path to learn ?

popeye16:02:02

Team I am getting

Caused by: java.lang.Exception: Cyclic load dependency:

popeye16:02:19

i understand that using namespace one in another

popeye16:02:09

is there anyway to avoid it? I wanted to use one of the function from another namespaces, which already I imported the file in another

pavlosmelissinos16:02:16

@popeyepwr Imho, this is usually a sign that you need a namespace restructuring. In other words, think again whether you really need a relationship like ns a -> ns b -> ns c -> ns a (read -> as requires). Usually the part of a that requires b is higher level than the part that c is using. So, assuming that all of the affected namespaces are under your control, it might make sense, depending on the case, to either: • further break them down (to split the higher from the lower level stuff of a) or • merge the interconnected parts into a single namespace

👍 3
Michael Stokley16:02:18

extract out the shared code into a new namespace

clyfe16:02:07

There's also a technique by which stuff is moved at runtime ((resolve 'my.nsp/f) arg1 arg2) generally not good practice.

👍 3
clyfe16:02:05

Of interest, there is also Timothy Baldridge's answer here (use interfaces and implementations): https://groups.google.com/g/clojure/c/-PvQvVmmLQg

💯 3
mathpunk18:02:13

I'm trying to do a reduction (or something else.... catamorphic? lots into 1) but I'm having trouble figuring out exactly how to shape it. The first def is an example from the docs of a library I'm trying out, and the second is the way that my data is shaped.

(def g1 (-> (dep/graph)
            (dep/depend :b :a)   ; "B depends on A"
            (dep/depend :c :b)   ; "C depends on B"
            (dep/depend :c :a)   ; "C depends on A"
            (dep/depend :d :c))) ; "D depends on C"

(def my-data {:a #{:b :c}
              :b #{:d :c}
              :d #{}})

mathpunk18:02:32

The end goal is a dependency graph of all that data

mathpunk18:02:25

There's a sort of double higher order fn going on: for every key, I want to put all its dependents into this growing dependency graph

mathpunk18:02:51

maybe I should be using a loop-recur form

hiredman18:02:52

You have basically two options 1. Reduce over the map, reducing over each map entry 2. Transform your map into a more linear structure, then reduce over that

mathpunk18:02:17

i'm getting thrown by f should be a function of 2 arguments in reduce's docs. This is a 3 argument function. At first I thought, use partial, but the first argument of dep/depend is that graph that's accumulating

hiredman18:02:42

it is two arguements

hiredman18:02:53

the graph and a map entry (which is a pair)

hiredman18:02:40

the above is an example of choice 2

mathpunk18:02:26

so the linearized version of what i started with would be something more like

[{:depends :a, :depends-on :b}
 {:depends :a, :depends-on :c}
 {:depends :b, :depends-on :d}
 {:depends :b, :depends-on :c}
 {:depends :d}]
?

hiredman18:02:09

or even just a bunch of pairs like [:a :b], the relationship implied by the context

nice 3
hiredman18:02:08

when "linearizing"(normalizing? it is connected in some way to normalizing data for a rdms) your structure like that you have two choices in how to do it, via seqs or via transducers

hiredman18:02:10

using a for like in my example produces a lazy seq, lazy seqs are sort of a habitual thing to grab for, but transducers are often faster, use less memory, and behave nicer when mixed with io/side effects

mathpunk18:02:45

when i finally run this function, i anticipate running it approximately once. so i'll skip the transduction

mathpunk18:02:10

so where you have that empty map in your db def, i would have (dep/graph), which is the empty val for my case

mathpunk18:02:26

cool, it was not hard to linearize this, and that does make it easier to think about

mathpunk18:02:43

(defn linearize [[component references ]]
  (for [ref references]
    [component ref]))


(def normalized (mapcat linearize adjacency-list))


(def dependency-graph
  (reduce (fn [g entry] (dep/depend g (first entry) (second entry))) (dep/graph) normalized))
Piece of 🍰 , thanks hm!

popeye19:02:18

Team I have below var

(def var [{:a "a1":b "b1" :c "c1"}
 {:a "a2" :b "b2" :c "c2"}])

popeye19:02:37

want to collect the values like below

popeye19:02:44

[[a1 b1 c1] [a2 b2 c2]]

popeye19:02:18

cannot use vals

popeye19:02:49

wanted to fetch according to keys

popeye19:02:03

I tried something like

(mapv #(get var %) [:a :b :c])

popeye19:02:15

but did not help

popeye19:02:48

thing is

:a :b :c

popeye19:02:56

will not be in order in each map

hiredman19:02:20

var is 1. a really bad name to use because it conflicts with the var special form 2. a vector of maps

popeye19:02:22

so need to pull it from each map,

hiredman19:02:47

so for example (get [{:a 1}] :a) is not going to return anything useful

hiredman19:02:15

which is what your map is doing

popeye19:02:35

(def var {:a "a1":b "b1" :c "c1"})
=> #'user/var
(mapv #(get var %) [:a :b :c])
=> ["a1" "b1" "c1"]

jonathan19:02:13

hi, I'm struggling with hyphens in clojure.java.jdbc. I'm using SQLITE and have a column user_id. I'm doing a simple insert like this:

(jdbc/insert! db :user {:user-id 555} {:identifiers #(.replace % \- \_)})
Getting the error: SQL error or missing database (near "-": syntax error)

seancorfield19:02:08

:identifiers deals with names coming back from SQL to Clojure; :entities is what you want for names going from Clojure to SQL.

jonathan19:02:04

thanks sean wow really nice to get a reply from the author so fast!

jonathan19:02:44

would love to use next.jdbc but due to circumstances out of my control I need to target JDK7

seancorfield19:02:45

I try to keep an eye out for SQL-related Qs here in #beginners -- posting in #sql is certain to get my attention more quickly tho'... 🙂

jonathan19:02:10

that worked thanks

jonathan19:02:35

tried using that :identifiers key to fix the hyphens but it seems too have no effect

popeye19:02:43

if vector as multiple map values thn it shouls extract

lispyclouds19:02:52

So the issue is that for each of the keys [:a :b :c] you need to call (get) on each of your maps. so its a nested iteration

lispyclouds19:02:52

you need to do it the inner fn that youre passing to mapv for each of your keys

lispyclouds19:02:56

see if using https://clojuredocs.org/clojure.core/for in the mapping function helps?

hiredman19:02:57

get looks some thing up by a key in the immediate thing you give it, it will not reach down through other collections to look for something with a key

popeye20:02:27

@hiredman I wrote this

(map  (fn [x] (mapv #(get x %) [:a :b :c])) var)

hiredman20:02:53

Looks nice, you might want to use mapcat, and again, don't use the name "var" it conflicts with the var special form, and will act really odd in some situations

popeye20:02:39

yeah sure, i have something else in my code

zackteo22:02:58

Hello, am currently trying to prototype a simple clojure backend with Ring. Am using https://github.com/lispcast/listronica/tree/1.17 as a template. Are there any resources I might want to look at? How do I best create an API to transmit edn from a clojurescript webapp to ring backend? Can I just put edn in the :body of a route? That gives me some errors. Do I wrap it in a string like "{:test [\"test\"]}"

ej22:02:49

I'm reading Web development with Clojure (3rd ed.) and I noticed that the book starts off with what I would consider traditional web development, then introduces AJAX with ClojureScript, before adding reagent and eventually re-frame. I love the book, but this seems like a lot of additional complexity to me, and I'm wondering if I need that additional complexity. I want to make simple and fast websites with text fields for inputting some data, and some buttons and checkboxes for filtering said data. If I need fancy animations I can use CSS. I don't think I need reactivity, but maybe I would be missing something else? Is the workflow of using reagent (with reframe?) somehow more productive than just writing Clojure on the server and producing HTML? Asked in another way, what are some good use cases for ClojureScript with reagent?

seancorfield22:02:15

"Modern" web apps these days are SPAs (single page applications) that communicate with a REST-like backend -- and that's what the book is aiming at. It's why I don't recommend that book -- or the Luminus template -- for people who are brand new to Clojure: it's just too many moving parts. Take a look at the Practicalli stuff for web development and start simple with just Ring and a few small, simple libraries. Then as you understand that process, you can add complexity as you need it.

ej23:02:09

I will take a look, @seancorfield. Thanks for the suggestion! I think my question could be asked as "How do you decide if an SPA is the right fit?", if anyone has input on that instead 🙂

seancorfield23:02:27

I think you've answered your own question there @endrejoh -- if you want to make simple/fast websites with forms for capturing data and storing it somewhere, then SSR (server-side rendered) HTML is going to be just fine and Ring + Compojure + maybe Selmer + next.jdbc is about all you'll need (assuming a database for storage). Take a look at https://github.com/seancorfield/usermanager-example (or the Reitit-based variant linked in the README).

ej23:02:08

Thank you so much, @seancorfield!

evocatus23:02:04

I have a function that takes 3 arguments. How do I call it if I have arguments packed in a list?

Darin Douglass23:02:53

(apply my-fn args)

👍 7