Fork me on GitHub
#clojure
<
2020-04-07
>
datran02:04:18

Is there a built-in function to de-namespace a map? If I have #entity{id: 1 name: "hey"} I just want {:id 1 :name "hey"}

Breno Duque Estrada02:04:43

Can you just build a map without namespace? Or this namespaced map is already built?

datran02:04:26

It's already been built, I'm receiving it

datran02:04:52

But I don't necessarily know what keys I'll be getting, I just want the non-namespaced versions

Breno Duque Estrada02:04:01

Let me check it out

Chris O’Donnell02:04:06

(into {} (map (fn [[k v]] [(keyword (name k)) v])) m)

Chris O’Donnell02:04:31

(keyword (name :my.ns/foo)) -> :foo

Chris O’Donnell02:04:27

(There isn't a built-in function to do it that I know of, but you can do it with a short combination of core functions.)

Breno Duque Estrada02:04:47

There is an example

;;example of removing namespaces from all keys in a nested data structure
(def thing {:page/tags [{:tag/category "lslsls"}]})
(postwalk #(if(keyword? %)(keyword (name %)) %) thing)
{:tags [{:category "lslsls"}]}

Chris O’Donnell02:04:20

Good point, clojure.walk is useful if you're working with a nested map.

4
andy.fingerhut02:04:25

It is good to be sure that you are not receiving a map with multiple different qualifiers for the same 'base name' of keyword, e.g. using a function like the above if you received {:foo/id 1, :bar/id 2} would end up with one of those two key/value pairs in the result, not both, and it would not be easily predictable which one it was, unless you wrote explicit code to "pick the winner".

8
Breno Duque Estrada03:04:31

Yes! Or if you need just some keywords, can use a destruct and map only what you want

potetm02:04:13

@datran I would strongly advise against doing that.

potetm02:04:27

You’re 1. throwing away information, 2. introducing odd edge-cases (e.g. what @andy.fingerhut said), 3. adding runtime overhead, for what gain?

potetm02:04:13

If you’re trying to hook two already-written systems together, I would find it preferable to manually map keys somewhere. There’s no reason to assume that every key will line up in both systems anyways.

seancorfield02:04:39

@datran Why do you want to de-namespace the maps? Why not just use them as-is?

seancorfield02:04:14

Namespaced maps are idiomatic Clojure.

4
datran03:04:38

Ok, thanks for the questions. The reason I wanted to de-namespace was because the ns qualifier wasn't surviving the network boundary when I was encoding them to json. But @seancorfield and @potetm, you make good points! So I changed my encoding to transit and edn works perfectly out of the box, and now I don't have to worry about hinky de-namespacing functions

4
8
💯 4
👏 4
henrik07:04:11

In (get map key not-found) , is not-found eagerly evaluated?

Kevin07:04:39

Yes, it's still evaluated even if key is in map

👍 4
henrik07:04:01

Separate or it is, then.

g7s12:04:13

Keep in mind though that not-found and falsy are two different things, i.e. (get {} :a 42) and (get {:a nil} :a 42) will give you different results whereas (or (get {:a nil} :a) 42) and (or (get {} :a) 42) will give you the same result.

👍 8
henrik12:04:24

I try to never, ever put nils in maps.

andy.fingerhut08:04:00

In general, get is a function, and all Clojure function calls first evaluate the values of their arguments, then call the function. If something is a macro, then it is up to how the macro is defined if/when/how-many-times things are evaluated.

👍 4
henrik08:04:27

In this particular case, it might have been nice if get/get-in were macros. I struggle to think of a benefit of having not-found evaluated eagerly. Unless you’re hacking it to side-effect on it.

henrik08:04:06

(Very liberal interpretation of “benefit”)

andy.fingerhut09:04:07

In most cases I have seen, the not-found parameter value supplied in the call is a keyword, or a new JVM object created just for the purpose (sometimes called a 'sentinel value'), because that is guaranteed to be different than any other JVM object on the system, and thus never equal to anything that you might possibly find in the collection.

andy.fingerhut09:04:46

Most things in Clojure are immutable values, so most parameters are not evaluated for the purpose of achieving side effects.

henrik09:04:41

I’m getting something from cache (an atom), or otherwise fetching it and inserting it into the cache (as well as returning it). Semantically, it could be get , but it makes no sense to do so, of course.

henrik09:04:34

> Most things in Clojure are immutable values, so most parameters are not evaluated for the purpose of achieving side effects. Indeed, which is why it’s a shame that get isn’t lazy wrt not-found.

Nir Rubinstein08:04:17

Does anyone know if there are tools to create docs or readme's from spec?

hindol10:04:46

Hi. In a proxy, can I delegate to the super method of the same name?

(defn ws-listener
  [_request _response ws-map]
  (proxy [WebSocketAdapter] []
    (onWebSocketConnect [^Session ws-session]
      (.onWebSocketConnect this)              ;; <<< This does not work
      (when-let [f (:on-connect ws-map)]
        (f ws-session)))))

hindol10:04:16

Found the answer by peeking at ring-jetty9-adapter: https://github.com/sunng87/ring-jetty9-adapter/blob/master/src/ring/adapter/jetty9/websocket.clj Need to use proxy-super!

🙏 4
Spaceman12:04:42

Hi, I have an issue with importing a library in production

Spaceman12:04:57

It works when I import it in development

Spaceman12:04:39

In production, however, I get the error Syntax error macroexpanding at (metadata.clj:13:1). remote: Execution error (ExceptionInfo) at tech.jna.base/do-load-library (base.clj:158). remote: Failed to load library

Spaceman12:04:30

When importing: [libpython-clj.require :refer [require-python]] [tech.v2.datatype :as dtype]

emccue14:04:06

@pshar10 There is a #libpython-clj channel

👍 4
emccue14:04:32

i would ask there

Prometheus15:04:15

Hey I’m having trouble deploying my application to Docker. I’m suspecting some code I’ve written

(defn load-schema
  []
  (-> (io/resource "schema.edn")
      slurp
      edn/read-string
      (util/attach-resolvers (resolver-map))
      schema/compile))
Compiling si-be.handler Syntax error compiling deftype* at (flatland/ordered/set.clj:19:1). Exception in thread "main" Syntax error compiling deftype* at (flatland/ordered/set.clj:19:1).
This is the error I’m getting
FROM clojure RUN mkdir -p /usr/src/app WORKDIR /usr/src/app COPY project.clj /usr/src/app RUN mkdir -p /usr/src/app/resources COPY resources/schema.edn /usr/src/app/resources RUN lein deps COPY . /usr/src/app RUN mv "$(lein with-profile prod uberjar | sed -n 's/^Created \(.*standalone\.jar\)/\1/p')" app-standalone.jar EXPOSE 8080 CMD java -jar app-standalone.jar
Dockerfile
#!/bin/sh docker kill si-be-container docker rm si-be-container docker build -t si-be ~/Developer/spesielt-interessert/si-be docker run -p 8080:8080 --name si-be-container -d si-be``` Deploy script

noisesmith15:04:23

@peder.august what version of flatland ordered are you using?

noisesmith15:04:38

also - is that the full error output?

Prometheus16:04:23

@noisesmith It’s a dependency of lacinia (graphql). I’m pretty sure it has something to do with the schema or something? error looks like this

Prometheus16:04:24

Step 9/11 : RUN mv "$(lein with-profile prod uberjar | sed -n 's/^Created \(.*standalone\.jar\)/\1/p')" app-standalone.jar
 ---> Running in f18b213a4483
Compiling si-be.handler
Syntax error compiling deftype* at (flatland/ordered/set.clj:19:1).
Exception in thread "main" Syntax error compiling deftype* at (flatland/ordered/set.clj:19:1).
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7114)
	... (alot of output)
	at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException: Must hint overloaded method: toArray
	at clojure.lang.Compiler$NewInstanceMethod.parse(Compiler.java:8496)
	at clojure.lang.Compiler$NewInstanceExpr.build(Compiler.java:8058)
	at clojure.lang.Compiler$NewInstanceExpr$DeftypeParser.parse(Compiler.java:7934)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7106)
	... 165 more
Compilation failed: Subprocess failed
Uberjar aborting because jar failed: Compilation failed: Subprocess failed
mv: cannot stat '': No such file or directory
The command '/bin/sh -c mv "$(lein with-profile prod uberjar | sed -n 's/^Created \(.*standalone\.jar\)/\1/p')" app-standalone.jar' returned a non-zero code: 1
Unable to find image 'si-be:latest' locally

Prometheus16:04:26

But it works when I do

lein run

hiredman16:04:19

you likely are using different versions of java

Prometheus16:04:14

But I’m just using the dockerimage of clojure?

hiredman16:04:12

right, your local version of java is different from what is used in the docker image

hiredman16:04:20

which is why you get different behavior

Prometheus16:04:11

hmmm, ok, how do I check the java version in docker? and how do I change it?

Prometheus16:04:14

thank you btw

hiredman16:04:43

you should check to make sure your dependencies are up to date, newer versions of java mad some changes to reflection which newer versions of clojure needed updating to handle

Prometheus16:04:13

I got a reply from someone saying to change my lacinia version, since it had some issues

Prometheus16:04:38

It worked changing my version of lacinia

jumar16:04:50

This looks like a documented issue with flatland.ordered when upgrading to JDK 11: https://www.deps.co/blog/how-to-upgrade-clojure-projects-to-use-java-11/

Alex Miller (Clojure team)17:04:12

yeah, this is a known issue (I think actually introduced in Java 10) where compiled code against java.util.Collection is now ambiguous due to the introduction of a new default method. Java's typing is sufficient to avoid the ambiguity, but requires a type hint in Clojure to do so. (non-AOT'ed code won't see this issue)

jumar18:04:17

I've always thought that timbre logs exception's stacktrace when the exception is passed as the first arg - however, it's not working now:

(try (throw (RuntimeException. "boom")) 
     (catch Exception e #_(.printStackTrace e) (log/error e "ah")))
Anyone knows what's going on? (the .printStackTrace thing works properly)

Tyler18:04:39

Hi all, hoping to get some help with Java interop, trying to make a proper HashMap:

// Java Example found
  Map<String, Object> args = new HashMap<>();
  args.put("pid", "current");
  args.put("profileName", "Time Profiler");
  args.put("timeout", 6000*1000);
  driver.executeScript("mobile: startPerfRecord", args);
;; What I have so far
(.executeScript driver
                   "mobile: deepLink"
                   (doto (java.util.HashMap. )
                     (.put "url" "")
                     (.put "package" "org.telegram.messenger")))
Clojure Error:
Unhandled java.lang.ClassCastException
   Cannot cast java.util.HashMap to [Ljava.lang.Object;
I suspect I'm not building the Map<String, Object> args = new HashMap<>(); correctly. My java skills aren't great, so not exactly sure what that's doing and haven't found any similar examples in Clojure. Do I need to add type hinting somewhere?

ghadi18:04:43

@slack1620 clojure maps are already implement java.util.Map

ghadi18:04:06

pass {"url" "the url" "package" "org.telegram.messenger"}

ghadi18:04:04

but anyways the error you're getting belies a misunderstanding

Tyler18:04:08

(.executeScript driver
                   "mobile:deepLink"
                   {:url ""
                    :package "org.telegram.messenger"})
Unhandled java.lang.ClassCastException Cannot cast clojure.lang.PersistentArrayMap to [Ljava.lang.Object;

ghadi18:04:16

it's saying "I want an array of objects"

Tyler18:04:27

let me try with strings as keys

ghadi18:04:29

"and you handed me a Map"

ghadi18:04:36

that will not work

ghadi18:04:44

let's debug scientifically

ghadi18:04:15

can you link to the Java doc of the method you're trying to call upon the driver class?

Tyler18:04:02

definition

public void executeCommand (final String command, final Map <String, Object> args) {
  this.driver.executeScript (command, args);
}

ghadi18:04:39

that is the signature of executeCommand, but you're calling executeScript

ghadi18:04:35

dammit @seancorfield i'm trying to use science

ghadi18:04:59

no hunches/guesses

ghadi18:04:28

harmonizes with your whistling

ghadi18:04:54

what class is driver -> find its documentation

Tyler18:04:16

public java.lang.Object executeScript(java.lang.String script,
                                      java.lang.Object... args)

ghadi18:04:45

so the X... varargs in Java translates at the calling convention level to an array of X

ghadi18:04:01

so you need to pass String, then an array of Object

ghadi18:04:23

you can make the latter with (into-array TheClass [.... elements])

ghadi18:04:39

(into-array Object [your strings here])

ghadi18:04:57

https://clojure.org/reference/java_interop#_arrays I don't see anything there ^ about varargs specifically

Tyler19:04:52

Still trying to follow.. So we only have one arg for our java.lang.Object... args . Is the hashmap Object the item for the array?

(into-array Object {:url ""
                    :package "org.telegram.messenger"})
I think you suggest the strings.
(into-array Object ["url" ""
                    "package" "org.telegram.messenger"])

ghadi19:04:42

(.executeScript driver "command" (into-array Object ["url" ""
                    "package" "org.telegram.messenger"]))

noisesmith19:04:34

there's also the object-array shorthand

👀 4
Tyler19:04:34

ok, that makes sense to me. thanks guys

Tyler19:04:09

I got it to work. Turns out it does need an array of HashMap since it will be converted to json object. Thanks for the help. Very subtle with the java.lang.Object... args

(.executeScript driver
                "mobile:deepLink"
                (object-array [(doto (java.util.HashMap.)
                                 (.put "url" "")
                                 (.put "package" "org.telegram.messenger"))]))

noisesmith19:04:28

you still shouldn't don't need to use interop there - {"url" "" "package" "org.telegram.messenger"} should work

noisesmith19:04:49

clojure hash-maps are Map instances as ghadi mentioned

Tyler19:04:01

I see, yes the (doto java.util.HashMap. ) is not needed but the (object-array [{"url "..."}]) is still needed since it's definition has variadic arguments.

ricardo.ekm21:04:28

Hi guys! I'm parsing command line arguments using https://github.com/clojure/tools.cli. It seems whenever it finds a comma inside a parameter value it splits it, and put in a separate arguments key. Any way to avoid this?

ricardo.ekm21:04:01

lein run --addressComplement "Cambridge, MA"

ricardo.ekm21:04:06

(println parse-opts args cli-options))

ricardo.ekm21:04:03

{:options {:addressComplement "Cambridge,}, :arguments [MA"], :summary...}

ricardo.ekm21:04:12

the weird thing is if I put "Cambridge,MA" with no spaces it works fine

ricardo.ekm21:04:37

but "Cambridge, MA" causes the unexpected behavior

OrdoFlammae21:04:40

Maybe it's the spaces that are causing the split?

ricardo.ekm21:04:53

It seems so! I'm looking for a way to avoid the split

noisesmith21:04:04

it's weird that it also captures the ""

noisesmith21:04:15

if you use prn rather than println you'll get the kind of printing the repl would do instead of the default which does weird things to strings

ricardo.ekm21:04:16

It seems Clojure tools cli is not aware of double quotes either.

kenj21:04:58

I have a server with long standing TCP client connections, and really like being able to update functions in the REPL without having to reconnect a test client. I’ve found you can pass a var to a callback which allows that var to be set to something else via the REPL and things work great. That said, making every callback and var and then having to call var-get seems like a pain. Is there a better solution here?

noisesmith21:04:17

you can just call vars

noisesmith21:04:26

you don't need var-get for that

noisesmith21:04:22

user=> (map #'str [:a :b :c])
(":a" ":b" ":c")

noisesmith21:04:53

vars implement IFn, and when called just call the thing they hold

kenj21:04:16

huh, I had no idea. I thought I had tried that yesterday but apparently I didn’t do it right.

kenj21:04:11

I guess that makes things easy then, I just have to remember to pass vars for all callbacks.

ricardo.ekm21:04:32

run --addressComplement "MyAddress"

hiredman21:04:04

that is likely your "run" script eating the quotes

ricardo.ekm21:04:05

(prn (parse-opts args cli-options))

hiredman21:04:16

lein run I guess

ricardo.ekm21:04:25

{:options {:addressComplement "\"MyAddress\""}

ricardo.ekm21:04:50

But quotes are important for command line arguments that include spaces on it

noisesmith21:04:04

I think the issue is you aren't seeing the grouping / parsing that a shell would do - as if you were executing this without sh

hiredman21:04:07

the coma splitting thing is some lein does

hiredman22:04:22

or not, I dunno, but basically, you will not get the results you would get just by running your code if you run your code from lein run

hiredman22:04:20

you might try something like

java -cp `lein classpath` clojure.main -m your.main your arguments here

ricardo.ekm22:04:34

Good hypothesis, I gave it a try here

ricardo.ekm22:04:48

however it seems the arguments arrive correctly

ricardo.ekm22:04:03

(defn -main [& args] (println args)

ricardo.ekm22:04:16

(--addressComplement "My, Address")

hiredman22:04:19

lein run is like a joke that has gone too far

hiredman22:04:32

I think someone earlier suggest you need to use prn

ricardo.ekm22:04:02

("--addressComplement" "\"My," "Address\"")

ricardo.ekm22:04:19

so the issue seems with clojure tools CLI

hiredman22:04:36

the issue is with lein run

ricardo.ekm22:04:41

{:options {:addressComplement "\"My,"}, :arguments ["Address\""]

noisesmith22:04:54

the println is hiding the error, prn exposes it

noisesmith22:04:28

are you using a shell that doesn't use " for grouping?

ricardo.ekm22:04:30

("--addressComplement" "\"My," "Address\"") seems right to me, isnt it?

hiredman22:04:48

lein run is like a hack, it is a covience thing that got added, but it is full of corner cases is not a good way to actual run your clojure programs

ricardo.ekm22:04:20

sorry, I missed it

ricardo.ekm22:04:31

whats the problem with that?

ricardo.ekm22:04:56

it split into two string

ricardo.ekm22:04:27

Yeah, bye bye lein run

hiredman22:04:47

~ % clojure -e '(prn *command-line-args*) (System/exit 0)'  -- --addressComplement "My, Address"
("--addressComplement" "My, Address")
~ %

ricardo.ekm22:04:49

from the shell it works nicely

noisesmith22:04:14

@ricardo.ekm just a hunch - what if you used lein trampoline run instead of lein run - this starts your process in the same jvm instead of forwarding args to the other vm

noisesmith22:04:37

I suspect the re-invocation of the the next vm is where the args get mangled

ricardo.ekm22:04:03

it yields the same problem 😕

seancorfield22:04:43

Just use the clojure CLI instead 🙂

ricardo.ekm22:04:51

BTW how do I set an option as required in clojure tools cli?

ricardo.ekm22:04:00

the docs talk about it, but I haven't figured out how to do it (I think an example is missing)

ricardo.ekm22:04:31

Instead of throwing errors, parse-opts collects error messages into a vector and returns them to the caller. Unknown options, missing required arguments, validation errors, and exceptions thrown during :parse-fn are all added to the errors vector.

By default, the error message when a required argument is omitted is:

Missing required argument for ...

bfabry22:04:35

a required argument is different than a required option

ricardo.ekm22:04:28

Hmmm, how do I set an argument as required?

bfabry22:04:13

the map at the end, :properties

bfabry22:04:28

well, :property value

bfabry22:04:05

alternatively, just having a long form of "--port PORT" specifies that it requires an argument

bfabry22:04:38

the docstring for the fn seems quite good

ricardo.ekm22:04:28

Hmmm, how do I set an argument as required?