Fork me on GitHub
#beginners
<
2019-04-30
>
Michael Stokley00:04:17

in python, you can form strings with a triple-double quote and then don't need to escape double quotes in the string

Michael Stokley00:04:22

does clojure have an equivalent?

Ivan Koz00:04:58

no, not yet? this feature is in development for java, maybe clojure will adapt it then

noisesmith00:04:11

Clojure doesn't cross compile to Java, it produces byte code directly, so having a """ feature in Java doesn't really help Clojure

Ivan Koz00:04:12

at-least clojure can use jep as guideline

andy.fingerhut00:04:40

Triple-quoted strings have been around a lot longer in Python, and people over the years have occasionally asked about adding such a thing to Clojure. I don't even see an issue in JIRA requesting it, though, probably because no one felt strongly about it enough to create one. Even if they did, it may not be considered important enough by the Clojure core team to ever add such a capability.

Ivan Koz00:04:40

well clojure has edn to describe pretty much any data and macros for DSL's

Ivan Koz00:04:05

java is less flexible in that area

Ivan Koz00:04:39

as i see it

alexmiller00:04:58

From previous conversations, Rich is not interested in adding this

👍 4
alexmiller00:04:59

I don’t remember, long time ago

Ivan Koz00:04:11

seems like a good addition for docstrings tho

alexmiller00:04:41

Is there enough value there to be worth adding syntax? I’d say no, but that’s just my opinion.

alexmiller00:04:21

Adding syntax means breaking anything that relies on parsing syntax (as they no longer understand the new thing)

alexmiller00:04:05

Clojure has the benefit of being the “parser” for a higher percentage of use than other language, but it’s not the only player

seancorfield01:04:15

@nxtk It's also important to recognize that

"""
Hello, World!
"""
is already valid Clojure -- and is parsed as three consecutive strings.

seancorfield01:04:47

Two empty strings with this as the middle string "\nHello, World!\n"

hiredman01:04:44

That was about ten years ago, and there is at least one other thread about heredocs on the clojure group, and one on the clojure dev group, and at one point there was a wiki page (not sure about content)

tabidots03:04:18

Is there a way to rewrite this using r/fold? It is part of an integer factorization algorithm, and async.profile is telling me the bottleneck is in first and rest when this function is called a large number of times. The function should terminate early if n is ever “whittled down” to 1, and must return nil if 1 is not obtained after all keys in prime-map are iterated over. Note: n is not the integer to be factorized here, but if the original input number is sufficiently large, it will generate many such n (potentially thousands) to be run through this function.

tabidots03:04:21

It’s kind of a reduce with two accumulators. Is there any way to do this without introducing unnecessary creation of intermediate data structures?

tabidots03:04:23

I guess it doesn’t have to be r/fold; the number of keys in prime-map will generally not exceed 512. I mean, they could, but at that point, I should use another factorization algorithm altogether 😅

tabidots04:04:06

I managed to eke out some performance gains by (1) using a transient to hold all the outputs of this function for many n as they are being accumulated (note: Not the intermediate outputs of the loop for a single n, because they must be a sorted-map) (2) passing the original generated list of primes '(2 3 5...) as a separate argument, instead of taking (keys m) every time. (Guess iterating over $keySeq is slower than a normal list.) I think that’s about as far as I can get.

Joel04:04:05

MyJavaClass/class -> shouldn't this be the same as MyJavaClass.class in Java?

Joel04:04:33

(I get Unable to find static field)

haus05:04:46

the / notation works for static methods and fields

seancorfield05:04:46

MyJavaClass.class is "special" tho', right? In Clojure MyJavaClass on its own is the class object.

haus05:04:02

that's right

seancorfield05:04:12

In Java, you can do stuff like String.class.getDeclaredMethods() but in Clojure that is just (.getDeclaredMethods String)

seancorfield05:04:30

If you have an instance (an object) you can get the class with myObj.getClass() in Java or (.getClass myObj) or just (class myObj) in Clojure.

Joel06:04:41

i gathered from what you said I can just literally write MyJavaClass and that is the same as Java: MyJavaClass.class

Joel06:04:59

in REPL this just says: com.some.code.MyJavaClass

seancorfield06:04:56

Sounds about right @joel380 -- Clojure's all about the "simple" 🙂

evocatus07:04:51

what is the best way to convert str to int?

evocatus07:04:55

ok, Integer/parseInt works for single values but doesn't work inside map. How to explain it?

arbscht08:04:23

(Integer/parseInt ...) is read as a static method invocation. Integer/parseInt by itself is read as static field access. parseInt being a java static method (which isn't a clojure fn) and not a static field, means it can't be mapped directly

arbscht08:04:49

you can lift the interop invocation into a clojure fn like this: (map #(Integer/parseInt %) ...)

deep-symmetry15:05:52

Thanks for the link, voted!

eskemojoe00716:04:57

I've got a function that should return false if it can't find anything and should return the seq if it can. I've done it this way

(defn should-score?
  "This function determines whether a hand should score or not.
  It returns the rank of the scoring cards or false if it shouldn't score."
  [hand]
  (let [scoring-rank (->> hand
                       (map ::rank)
                       frequencies
                       (filter #(= (second %) 4))
                       (map first))]
    (if (empty? scoring-rank)
      false
      scoring-rank)))
Is there a more concise way to do the returning at the end...it seems a bit like overkill.

mtkp16:04:02

if nil is okay instead of false, then not-empty might be helpful

eskemojoe00716:04:41

nil is fine! That's perfect.

👍 4
Ivan Koz16:04:53

@david.folkner can you provide some sample input data for that function?

eskemojoe00716:04:50

Yeah

[{:rank 8, :suite :heart}
 {:rank :ace, :suite :heart}
 {:rank 8, :suite :spade}
 {:rank 9, :suite :heart}
 {:rank 3, :suite :club}]

eskemojoe00716:04:38

With the advice I simplified it to:

(defn should-score?
  "This function determines whether a hand should score or not.
  It returns the rank of the scoring cards or false if it shouldn't score."
  ([hand] (should-score? hand 4))
  ([hand num]
   (->> hand
        (map ::rank)
        frequencies
        (filter #(>= (second %) num))
        (map first)
        not-empty)))

Ivan Koz16:04:30

@david.folkner but it returns seq of nil for your sample data

Ivan Koz16:04:56

do you have a list of scoring conditions?

eskemojoe00716:04:25

Yes...the sample data is not scoring. (making go-fish game). So if they have 4 8's they score.

[{:rank 8, :suite :heart}
 {:rank :ace, :suite :heart}
 {:rank 8, :suite :spade}
 {:rank 8, :suite :club}
 {:rank 8, :suite :diamonds}
 {:rank 9, :suite :heart}
 {:rank 3, :suite :club}]
would be a scoring hand

Ivan Koz16:04:44

so for cards 8 8 8 8 4 4 4 3 2 we should return a seq of (8, 4) if winning condition is >= 3?

eskemojoe00716:04:07

I think that function does that.

eskemojoe00716:04:04

Oh, I appologize. I've been messing with spec...so the input should be:

[{::rank 8, ::suite :heart}
 {::rank :ace, ::suite :heart}
 {::rank 8, ::suite :spade}
 {::rank 8, ::suite :club}
 {::rank 8, ::suite :diamond}
 {::rank 4, ::suite :spade}
 {::rank 4, ::suite :club}
 {::rank 4, ::suite :diamond}
 {::rank 9, ::suite :heart}
 {::rank 3, ::suite :club}]

dkrieger16:04:51

Sorry to interrupt -- Is there a way to see what threads are running on JVM from clojure repl? I ran this in the repl and think the 4 threads involved must still be running...

(let [my-atom (atom 0)]
  (dotimes [i 3]
    (doto (Thread. (fn []
                     (Thread/sleep (* 1 1000))
                     (swap! my-atom inc)
                     (recur)))
      .start))
  (str @my-atom "bar" "baz")

  (doto (Thread. (fn []
                   (Thread/sleep 5000)
                   (println @my-atom)
                   (recur)))
    .start)
  )

noisesmith16:04:41

@dougkrieger (Thread/getAllStackTraces) returns a hash map from every thread to its stack trace

noisesmith16:04:10

if you call seq on the stack trace you get something semi-intelligible

noisesmith16:04:30

user=> (->> (Thread/getAllStackTraces) vals (mapv seq) pprint)
[([java.lang.Thread dumpThreads "Thread.java" -2]
  [java.lang.Thread getAllStackTraces "Thread.java" 1610]
  [user$eval248 invokeStatic "NO_SOURCE_FILE" 1]
  [user$eval248 invoke "NO_SOURCE_FILE" 1]
  [clojure.lang.Compiler eval "Compiler.java" 7176]
  [clojure.lang.Compiler eval "Compiler.java" 7131]
  [clojure.core$eval invokeStatic "core.clj" 3214]
  [clojure.core$eval invoke "core.clj" 3210]
  [clojure.main$repl$read_eval_print__9068$fn__9071 invoke "main.clj" 414]
  [clojure.main$repl$read_eval_print__9068 invoke "main.clj" 414]
  [clojure.main$repl$fn__9077 invoke "main.clj" 435]
  [clojure.main$repl invokeStatic "main.clj" 435]
  [clojure.main$repl_opt invokeStatic "main.clj" 499]
  [clojure.main$main invokeStatic "main.clj" 598]
  [clojure.main$main doInvoke "main.clj" 561]
  [clojure.lang.RestFn invoke "RestFn.java" 397]
  [clojure.lang.AFn applyToHelper "AFn.java" 152]
  [clojure.lang.RestFn applyTo "RestFn.java" 132]
  [clojure.lang.Var applyTo "Var.java" 705]
  [clojure.main main "main.java" 37])
 nil
 ([java.lang.Object wait "Object.java" -2]
  [java.lang.ref.ReferenceQueue remove "ReferenceQueue.java" 143]
  [java.lang.ref.ReferenceQueue remove "ReferenceQueue.java" 164]
  [java.lang.ref.Finalizer$FinalizerThread run "Finalizer.java" 212])
 ([java.lang.Object wait "Object.java" -2]
  [java.lang.Object wait "Object.java" 502]
  [java.lang.ref.Reference tryHandlePending "Reference.java" 191]
  [java.lang.ref.Reference$ReferenceHandler run "Reference.java" 153])]
nil

dkrieger18:04:09

so your code is dumping the running thread stack traces, taking just the vals (so the traces, not the thread objects), and runs seq on each value? is mapv any different from map in this context (isn't there only one collection)? The only hints I'm seeing are some lines with "NO_SOURCE_FILE", which would seem to match up with repl usage. I suppose the next step would be to modify the output to include the thread identifier next to the stack trace line so I know what to kill

noisesmith18:04:56

yeah, the thread names will look arbitrary, but the thread objects themselves have a cancel method that works if the thread is currently sleeping

noisesmith18:04:15

I used mapv rather than map reflexively, as there's no benefit to laziness here

noisesmith18:04:37

mapv vs. map isn't about the number of collections, it's about the data type / laziness of the return value

noisesmith18:04:36

mapv instead of map won't introduce hidden bugs, map instead of mapv will (with the exception of cases where laziness is required, which is relatively rare)

dkrieger18:04:01

ah ok, thanks. is it idiomatic to use map(v) to iterate if I'm only interested in side-effects?

noisesmith18:04:22

for side effects use doseq or run!

Ivan Koz17:04:17

@dougkrieger if you are limited to hotspot you can access private native api

(let [thread-bean (ManagementFactory/getThreadMXBean)
      method (.getDeclaredMethod (class thread-bean) "getThreads" nil)]
  (. method (setAccessible true))
  (seq (.invoke method thread-bean nil)))

👍 4
noisesmith17:04:10

these same thread objects are accessible via the public Thread/getStackTraces method (they are the keys on the hash it returns)

noisesmith17:04:39

in my experience the stack traces are more useful, as they show what's actually running

dkrieger17:04:19

Thanks, I'll give this a shot when I get back to my desk

Ivan Koz17:04:20

yeah totally, just listed the option

Ivan Koz17:04:38

getStackTraces will do for development purposes

Ivan Koz17:04:39

(#object[java.lang.Thread 0x45584512 "Thread[clojure-agent-send-off-pool-6,5,main]"]
 #object[java.lang.Thread 0x42239010 "Thread[nRepl-session-bfa1d2a6-140c-4421-8926-f4b0d0a3ac9a,5,main]"]
 #object[java.lang.Thread 0x4b194328 "Thread[nRepl-session-fcb40158-17cb-40fd-826f-9b101ab234a2,5,main]"]
 #object[java.lang.Thread 0x13f13997 "Thread[clojure-agent-send-off-pool-4,5,main]"]
 #object[java.lang.Thread 0x14068e2e "Thread[clojure-agent-send-off-pool-3,5,main]"]
 #object[java.lang.Thread 0x4afb3fae "Thread[clojure-agent-send-off-pool-2,5,main]"]
 #object[java.lang.Thread 0x4b7a35da "Thread[Attach Listener,9,system]"]
 #object[com.intellij.rt.execution.application.AppMainV2$1 0x710a0358 "Thread[Monitor Ctrl-Break,5,main]"]
 #object[java.lang.Thread 0x629f5498 "Thread[Signal Dispatcher,9,system]"]
 #object[java.lang.ref.Finalizer$FinalizerThread 0x45b436d5 "Thread[Finalizer,8,system]"]
 #object[java.lang.ref.Reference$ReferenceHandler 0x7ca65e74 "Thread[Reference Handler,10,system]"]
 #object[java.lang.Thread 0xf4b86dd "Thread[main,5,main]"])

eskemojoe00717:04:32

It seems dumb, but I wanted to put a watch on an atom that updates the atom itself if certain conditions are met. That way anytime the atom changes, it checks for those conditions, then updates itself if they are met. Is that a terrible idea? Or even possible?

Michael Stokley17:04:30

sounds like the functional/reactive paradigm you see in react and vue

noisesmith17:04:37

I might quibble with "updates itself" as a watcher isn't the atom it watches, but calling swap! on an atom inside its watcher is valid (you'd want a guard to prevent infinite recursion of course)

🤐 4
Ivan Koz17:04:46

@david.folkner

(def x (atom 0))
=> #'playsync.core/x
(add-watch x :update-watch (fn [_ ref _ new-state]
                             (when (and (< new-state 50) (= (mod new-state 5) 0))
                               (swap! ref (partial + 5)))))
=> #object[clojure.lang.Atom 0x79e05e1e {:status :ready, :val 0}]
(swap! x inc)
=> 1
(swap! x inc)
=> 2
(swap! x inc)
=> 3
(swap! x inc)
=> 4
(swap! x inc)
=> 5
(swap! x inc)
=> 51

👍 4
Ivan Koz17:04:01

(< new-state 50) is our base case so recursion wont go over 50

noisesmith17:04:06

another example

(ins)user=> (def a (atom 0))
#'user/a
(ins)user=> (add-watch a :always-even (fn [_ r _ n] (when-not (even? n) (swap! r inc))))
#object[clojure.lang.Atom 0x1e044120 {:status :ready, :val 0}]
(ins)user=> @a
0
(ins)user=> (reset! a 5)
5
(ins)user=> @a
6
(ins)user=> (swap! a + 3)
9
(ins)user=> @a
10

👍 8
noisesmith18:04:36

and just for fun:

(ins)user=> (add-watch a :always (fn [_ r _ n] (swap! r inc')))
#object[clojure.lang.Atom 0x285d851a {:status :ready, :val 0}]
(ins)user=> @a
0
(ins)user=> @a
0
(ins)user=> (swap! a inc)
Execution error (StackOverflowError) at user/eval1$fn (REPL:1).
null
(ins)user=> @a
1697
(ins)user=> @a
1697

noisesmith18:04:51

it's a new way to measure your maximum stack depth :D

dkrieger19:04:18

now that threading has clicked, it's freaking awesome! double arrow threads (what's the proper terminology?) is basically the same as golang template pipes. makes repl interaction a breeze

solf19:04:02

thread-last macro I think

👍 4
noisesmith19:04:17

yeah I use thread macros more in the repl than I do in code, because it makes it easy to build a pipeline piece by piece

☝️ 4
dkrieger21:04:25

when doing Java interop (or generally when using Java), if you try calling a non-existent method, does it say "No matching field found", or is that an indicator that my syntax is off?

noisesmith21:04:59

there's a few ways to get that error, not all of them look like interop at a first glance

eskemojoe00721:04:13

Struggling to come up with a way to cycle through a finite list forever. I have a seq of (def people ["David" "Steve" "Max"]). I need to make a function (next-person people <Name>) and get the next person in the list. If I run (next-person people "Max") it needs to return "David", or (next-person people "Steve") to return "Max".

noisesmith21:04:55

@david.folkner perhaps cycle would help?

noisesmith21:04:19

but if the only usage of the list is that lookup, I'd make a hash-map

eskemojoe00721:04:58

I saw cycle I just don't know how to get only the next person instead of using take

eskemojoe00721:04:15

(take 1 (drop n (cycle ["David" "Steve" "Max"]))) seems to work, but I have to keep track of n. I was hoping to be able to just put in the name of the previous one.

noisesmith21:04:22

@david.folkner this makes a hash-map, you can call it like a function

(ins)user=> (def call-list (make-precedance ["David" "Steve" "Max"]))
#'user/call-list
(ins)user=> call-list
{"Max" "David", "David" "Steve", "Steve" "Max"}
(ins)user=> (call-list "Max")
"David"
(ins)user=> (take 10 (iterate call-list "David"))
("David" "Steve" "Max" "David" "Steve" "Max" "David" "Steve" "Max" "David")

noisesmith21:04:47

(cmd)user=> (defn make-precedance [coll] (reduce (fn [m [x y]] (assoc m x y)) {(last coll) (first coll)} (partition 2 1 coll)))

noisesmith21:04:06

equivalent, perhaps too clever

(ins)user=> (defn make-precedance [coll] (reduce (partial apply assoc) {(last coll) (first coll)} (partition 2 1 coll)))
#'user/make-precedance
(cmd)user=> (make-precedance ["David" "Steve" "Max"])
{"Max" "David", "David" "Steve", "Steve" "Max"}

noisesmith21:04:43

I took a second look and realized that (fn [m [x y]] (assoc m x y)) is equivalent in this context to (partial apply assoc)

noisesmith21:04:32

even higher-orderer

(cmd)user=> (defn make-precedance [coll] (apply hash-map (last coll) (first coll) (apply concat (partition 2 1 coll))))
#'user/make-precedance
(cmd)user=> (make-precedance ["David" "Steve" "Max"])
{"Max" "David", "Steve" "Max", "David" "Steve"}

eskemojoe00721:04:49

That last one is much easier to read. I just finally figured out what you were doing in the first ones.

noisesmith21:04:53

and I've been misspelling precedence this whole time :/