Fork me on GitHub
#clojure
<
2021-05-24
>
eskos01:05:38

Not claiming this is any prettier, but it is different enough to warrant showing. Maybe use this as an excuse to read about a few useful functions in Clojure API? 🙂

(->> {:a [1 2 3] :b [4]}
     (mapcat (fn [[k v]]
       (map (partial vector k) v)))
     (map reverse)
     (reduce #(apply (partial assoc %1) %2) {}))

Ben Sless06:05:54

mapcat is a good start, but why go through laziness at all?

(into {} (mapcat (fn [[k vs]] (->Eduction (map (fn [v] [v k])) vs))) m)

Ivan Koz09:05:54

@UK0810AQ2 nearly all clojure functions operating on sequences are lazy

Ben Sless09:05:41

@UH16CGZC2 There are plenty of opportunities to go through the transducing arity if all you're using is the core library

Ivan Koz09:05:46

Can you show us non lazy solution?

Ben Sless09:05:19

The solution I provided is non lazy

Ivan Koz09:05:48

oh i see it now, thank you, i'll look closer

Ben Sless09:05:28

🙂 How to spot it: mapcat doesn't take m as an argument

Ben Sless09:05:40

Also, the inner map doesn't take vs as an argument, i.e. it returns a transducer, which is one of the two arguments to the eduction constructor

Ivan Koz09:05:25

right, i understand transducers concept, just never seen them in that context of laziness, thank you

Ben Sless09:05:02

In which context do you usually see them, then?

Ivan Koz15:05:26

composable, decoupling transformations

tlonist05:05:38

Is there any idiomatic way to group a map by its namespaced keywords? for instance, I’d like to divide {:user/id 1 :user/name "abc" :test/id 2 :test/name "def"} into {:user/id 1 :user/name "abc"} {:test/id 2 :test/name "def"}

👍 1
tlonist05:05:22

I could use filter with (= (namespace key) “key_name”), but wonder if there is more succinct way of doing it.

seancorfield05:05:48

dev=> (def m {:user/id 1 :user/name "abc" :test/id 2 :test/name "def"})
#'dev/m
dev=> (map #(into {} %) (vals (group-by (comp namespace key) m)))
(#:user{:id 1, :name "abc"} #:test{:id 2, :name "def"})
if you want the whole thing split by namespace in one go.

seancorfield05:05:08

But I would ask “Why do you need/want this?”

👀 3
seancorfield05:05:58

(i.e., what situation are you in that you’re getting a map with mixed namespace qualifiers where you need to treat those qualifiers as structural information rather than global identity?)

tlonist05:05:56

Thanks for answering! This happened when joining two tables, and using a portion of data from one table to make a token.

seancorfield16:05:35

I’d probably just use select-keys to pull out the specific fields I wanted, to make the selection explicit.

tlonist05:05:51

Thanks, that can also work.

borkdude12:05:41

Is this a bug?

user=> (intern *ns* (with-meta '*foo* {:dynamic true}))
#'user/*foo*
user=> (binding [*foo* 2])
Execution error (IllegalStateException) at user/eval233 (REPL:1).
Can't dynamically bind non-dynamic var: user/*foo*

p-himik13:05:44

Can't answer the question but can explain why it works that way, if it's useful. The ^:dynamic metadata is only taken into account when the def form is parsed. It's not read from the var. Instead (.isDynamic v) is called.

3
p-himik13:05:28

So in the above case, (.isDynamic #'*foo*) will return false.

p-himik13:05:34

And there's still a way to make it work dynamically:

user=> (.setDynamic #'*foo*)
#'user/*foo*
user=> (binding [*foo* 2])
nil

borkdude13:05:03

makes sense. no non-interop way of doing it I'm afraid?

p-himik13:05:14

Couldn't find it.

jaide14:05:39

Is there any minimal examples out there on SSR with Reagent?

Noah Bogart14:05:18

might wanna ask in #reagent if you don’t get any answers in here

max minoS14:05:19

I am trying to use Boot in my Mac (Big Sur), but for some reason it isn't letting me due to this bug (using the Homebrew version): https://github.com/boot-clj/boot/issues/765 Then, I cloned the repo in GitHub, which apparently already has the fix included, and tried building it using make install, but for some reason it is still giving me the same error. When I navigated to the file with that error and removed the error check completely, it still gave me the commented out error; so I think I didn't build it correctly. Can someone help me build it so that it will not just use the version? I couldn't understand what they had in the CONTRIBUTIONS.md for this situation

p-himik15:05:28

How do you launch boot ? Not sure if the command exists on Mac, but does which boot output the right path to the new binary?

max minoS15:05:54

I created a shell script that points it to java -jar /path/to/boot.jar

max minoS15:05:18

actually, I will compile it again now and check

max minoS15:05:04

which boot returns ~/.bin/boot, which is where I placed my shell script

max minoS15:05:13

#!/bin/bash
export JAVA_HOME="${JAVA_HOME:-/opt/homebrew/opt/openjdk}"
declare -a "options=($BOOT_JVM_OPTIONS)"
exec "${JAVA_HOME}/bin/java" "${options[@]}" -Dboot.app.path="${HOME}/.bin/boot" -jar "${HOME}/Applications/boot/bin/boot.jar" "$@"
this is the shell script, which I also found was the shell script created by Homebrew, except the JAR file was compiled by myself

p-himik15:05:44

If you're absolutely sure that that jar at "${HOME}/Applications/boot/bin/boot.jar" is the one you compiled and that it doesn't have that error detection code, then it's very strange. Maybe removing ~/.bin/boot can help, no clue. There are also #boot-dev and #boot - maybe someone there knows.

max minoS15:05:09

yes, I am very sure that that is where the boot.jar is generated, and the ~/.bin/boot was also created by me (it's a folder in my PATH); thanks, I will ask there

Sam Ritchie15:05:01

hey all - is there a most efficient way to get the greatest element out of a sorted map?

Noah Bogart15:05:38

if it’s sorted, last?

Sam Ritchie15:05:06

I was under the impression that that walks the entire sequence of pairs, so has O(n) performance

Joshua Suskalo15:05:11

No, last is implemented on the sequence abstraction, which means it always iterates the full list.

Sam Ritchie15:05:32

peek for sorted maps is the goal here

Noah Bogart15:05:48

oh my apologies, you said map

wotbrew15:05:10

you can use rsubseq or subseq @U017QJZ9M7W if you are testing the key

Sam Ritchie15:05:12

“sorted map”, ie, sorted-map

👍 3
Joshua Suskalo15:05:30

rseq returns a reversed sequence in constant time

Joshua Suskalo15:05:43

so (comp first rseq) would get the last item in constant time.

👍 3
Joshua Suskalo15:05:53

For any reversible data structure.

Joshua Suskalo15:05:01

You can test for this with reversible?, but sorted maps qualify.

Sam Ritchie15:05:42

excellent, thank you!

djpowell15:05:54

Is Java 11 the recommended version for Clojure atm? Are there issues running on later JREs? (just want to pick the right jre so I don't cause myself trouble)

dharrigan15:05:26

I run Clojure with Java 16, no issues observed at all.

Joshua Suskalo15:05:41

I run my clojure code on jdk 15 and everything (at least that I use) works fine

Alex Miller (Clojure team)15:05:18

officially, we test and recommend using Clojure on LTS releases, of which Java 11 is the latest

Alex Miller (Clojure team)15:05:34

unofficially, we test and have no known issues with non-LTS releases

djpowell15:05:46

thanks - i've been away for a while 🙂

Alex Miller (Clojure team)15:05:03

Java 17 is the next LTS release, scheduled for this fall

Joshua Suskalo15:05:40

I hope that we can get clojure support for project panama soon after it gets released

Alex Miller (Clojure team)15:05:33

what support is needed?

Joshua Suskalo15:05:27

I'm not sure yet, especially since as far as I know the api is far from stabilized. It might be that it just ends up being some stdlib stuff that clojure will support out the gate. My (very incomplete and possibly incorrect) understanding however is that we'll need some way to declare foreign functions, and that it would be out-of-band from normal execution.

Alex Miller (Clojure team)15:05:39

seems like all the jeps for it have been released as of Java 16?

Joshua Suskalo15:05:48

Oh, I guess I'm out of date then.

Joshua Suskalo15:05:22

I guess I need to go look into the jeps

Alex Miller (Clojure team)15:05:52

I'm not tracking this at all and have no idea what the gaps are between Java and Clojure. if you're interested in it, it would be great to discover that and write it up on http://ask.clojure.org if there is some gap to close

Joshua Suskalo15:05:41

I'll start doing some research and see if there's anything that needs to be added.

ghadi15:05:56

there's no support necessary AFAICT

ghadi15:05:03

just a library you can use

Alex Miller (Clojure team)15:05:52

looks like there are Java 17-EA builds with everything at https://jdk.java.net/panama/

Joshua Suskalo15:05:14

That's really cool if it's just a library.

emccue16:05:57

yeah - no support needed, but there might be reasons to want maybe support for deftype metadata to make a primitive class

emccue16:05:21

so like

(deftype ^{:primitive true} Thing [a b])
would disallow ^:volatile and ^:unsynchronized-mutable on fields but generate a primitive class

emccue16:05:49

the actual bytecode for calling a method on a primitive class should be the same as usual

emccue16:05:56

beyond that there might be things like type hints being able to distinguish between stuff like Instant.ref and Instant.val

emccue16:05:39

wait i am thinking of valhalla, ignore me

emccue16:05:48

https://github.com/clj-python/libpython-clj <- whatever this project ends up doing with regards to panama is likely to be the way to go

ghadi17:05:10

keep in mind that the JVM doesn't generally break things intentionally, so anything a "dusty old jar" does is likely to remain working in the future

Max18:05:29

Is there an existing fn/macro in the standard library for the following pattern? It’s kind of like update, but for vars not maps.

(if condition (some-fn x) x)
If not, what would you call it?

ghadi18:05:31

(cond-> x condition (expr-that-uses-x ...))

Max18:05:42

I’ve used cond-> when I have multiple expressions, but it feels weird to use it for a single expression?

Michael Gardner18:05:37

why would it be weird?

ghadi18:05:51

it becomes a judgement call

vncz18:05:41

I’ve used it for single expressions as well and to me it just makes sense

noisesmith18:05:59

I've called this when-pred

(defn when-pred
  [test? f]
  (fn apply-when-matches
    [x]
    (if (test? x)
      (f x)
      x)))
but I really think this depends on the coding style you and your collaborators are comfortable with - I use this when my collaborators are as fond of higher order functions as I am

walterl18:05:13

It definitely makes sense, and I've used it for single expressions too, but when I ask myself what reads easier, I revert back to if every time 😅

noisesmith18:05:58

also - depending on context, this can easily become a multimethod

BuddhiLW20:05:40

I'm using closh to make a script which converts a "pattern<number>.ext" => "pattern<(- number constant)>.ext". I run in the following problem:

$ (map #'first ["teste1.txt" "teste{2..3}.txt" "teste2.txt" "teste3.txt"])
(\t \t \t \t)
$ (map #'id-extension ["teste1.txt" "teste{2..3}.txt" "teste2.txt" "teste3.txt"])
Error printing return value (ClassCastException) at clojure.string/split (string.clj:219).
class java.lang.Character cannot be cast to class java.lang.CharSequence (java.lang.Character and java.lang.CharSequence are in module java.base of loader 'bootstrap')
$ ls |> (first)
"teste1.txt"
$ ls |> (first) |> (id-extension)
"1.txt
That is, the function:
(defn id-extension [name]
  (->
   (map #(str/split % #"teste") name)
   (first)
   (second)))
Only works if I apply it to a single object. If I use map on it, it throws me this error. Does anyone know how do I fix this? OBS:
$ ls |> (identity)
("teste1.txt" "teste{2..3}.txt" "teste2.txt" "teste3.txt")

manutter5120:05:08

You have too many map s in your code. The outer most map calls id-extension with each filename from your vector. Then, inside id-extension, you are calling map again, which means you’re taking each individual character in the file name, and trying to pass it to str/split. Get rid of the map inside id-extension and just call str/split directly.

manutter5120:05:08

name is a built-in Clojure function, that may be giving you problems, try naming your argument filename or something.

BuddhiLW20:05:31

Yeah, I really don't get it.

$   (defn id-extension [filename]
#_=>     (->
#_=>      (str/split filename #"teste")
#_=>      (first)
#_=>      (second)))
#_=> 
#'user/id-extension

$ ls |> (first) |> (map #(str/split % #"teste")) |> (first) |> (second)
"1.txt"

$ ls |> (id-extension)
Execution error (ClassCastException) at user/eval6839$id-extension (REPL:0).
class clojure.lang.Cons cannot be cast to class java.lang.CharSequence (clojure.lang.Cons is in unnamed modu
le of loader 'app'; java.lang.CharSequence is in module java.base of loader 'bootstrap')

manutter5120:05:10

Hmm, what’s the code that’s calling id-extension now?

seancorfield20:05:32

You might want to try working with these functions in a regular REPL first to make sure you understand how map works (and perhaps ask in #beginners where folks have opted-in to helping folks with stuff like this).

☝️ 3
👍 3
seancorfield20:05:47

The thing to bear in mind about closh is that |> always sends a sequence of lines, so ls |> (first) |> (map ..) sends a sequence containing only one element into the map function. That’s the difference from Clojure.

seancorfield20:05:37

Your id-extension function expects a string but ls |> (id-extension) sends it a sequence of strings.

seancorfield20:05:08

$ ls |> (identity)
("bin" "deps.edn" "scratch.clj" "script.clj" "src" "wheel")
$ ls |> (first)
"bin"
$ ls |> (first) |> (identity)
["bin"]
ls |> sends a sequence of strings into the (identity) call, and also into the (first) call — so the latter produces just a string — but ls |> (first) |> (identity) sends a sequence containing that string into the (identity) call.

BuddhiLW21:05:14

Ok, I see. Considering that, It worked

(defn split-teste [filename]
  (->
    (str/split filename #"teste")
    (second)))

$  ls |> (map split-teste)
("1.txt" "2.txt" "3.txt")

✅ 3
BuddhiLW21:05:36

I will head to the #beginners . Thank you.

BuddhiLW21:05:01

Also, reducing the quantities of maps, just like @U06CM8C3V said helped closing-in the solution

Daniel21:05:03

Hi there, is there a way to create a repl from inside a spring boot application written in clojure?

ghadi21:05:23

(sometimes you hear people say "socket REPL", which is a socket server that runs a REPL, but the socket server can run anything)

Daniel21:05:53

Alright thanks, going to give that a try

Daniel21:05:16

I’ve tried to add that properties into application.properties used by Spring but the socket server doesn’t appear to have been launched. @ghadi

ghadi21:05:17

the properties mechanism will only work if Clojure gets initialized. If you add a call to any method on clojure.java.api.Clojure it will do the trick

Daniel21:05:53

@ghadi Well I did try to invoke Clojure from -main but the socket server doesn’t appear to have been launched:

(defn -main [& [args]]
    (do (prn "Running with" args)
        (let [plus (Clojure/var "clojure.core" "+")]
          (prn (plus 4)))
        (SpringApplication/run krypto.core.Application (into-array [""]))))

ghadi21:05:12

Please paste the system property you are using

Daniel21:05:41

clojure.server.repl="{:port 5555 :accept clojure.core.server/repl}"

ghadi21:05:06

Are you launching spring from clojure? Or clojure from spring?

Daniel21:05:31

I’m launching clojure from spring. So this is how I launch it:

mvn spring-boot:run
Inside pom.xml are:
<dependency>
      <groupId>org.clojure</groupId>
      <artifactId>clojure</artifactId>
      <version>1.10.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.4.5</version>
    </dependency>
And
<plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>2.4.5</version>
      </plugin>

ghadi21:05:10

So what’s that -main method above, experiment?

Daniel21:05:43

It’s the entry point to the SpringApplication.

(:gen-class :name ^{org.springframework.boot.autoconfigure.SpringBootApplication true}
              foobar.core.Application))

ghadi21:05:51

if you control the entrypoint to this application, you can just launch the repl explicitly

ghadi21:05:49

and passing the arguments above as ordinary clojure data {:port 5555 :accept 'clojure.core.server/repl}

ghadi21:05:53

note the quote on the symbol

ghadi22:05:01

actually you'll have to pass :name as well

Daniel22:05:19

Thanks @ghadi that worked!

ghadi22:05:21

the system properties-based invocation is a bit magical. It's applicable where 1) Clojure is getting loaded on the classpath 2) but you don't necessarily control the entrypoint

Daniel22:05:52

I see, yeah, it didn’t work in this case I had to call start-server

ghadi22:05:51

(note that with the system properties approach, you do not have to call start-server at all -- I suspect your problem was not having the system property present)

Daniel22:05:14

Hmm, that’s what I suspected too, I did try to pass in the properties through the Spring app.properties file and tried to include that on the mvn invocation, ie:

mvn spring-boot:run -Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}"
But these didn’’t work

ghadi22:05:58

yeah I'm not a mvn-ologist but it's unclear to me if that -D applies to the mvn process, or the jvm that the mvn process launches

Daniel22:05:12

Oh wait, I need to specify -Drun.jvmArguments= instead

3
ghadi22:05:15

you can confirm what system properties exist on a running JVM by calling: jcmd <the pid> VM.system_properties | grep clojure