Fork me on GitHub
#beginners
<
2021-01-25
>
roelof08:01:45

hmm, seeing something really wierd

roelof08:01:16

I installed cljs but when I want to use it , it not found

[email protected]:~/learn-reagent-course-files/giggin$ yarn global add shadow-cljs
yarn global v1.22.5
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...

success Installed "[email protected]" with binaries:
      - shadow-cljs
Done in 0.81s.
[email protected]:~/learn-reagent-course-files/giggin$ shadow-cljs -h
shadow-cljs: command not found

solf08:01:28

That's off-topic, as it's nodejs/yarn related. You can try yarn global bin, that will show you where yarn puts the binaries it installs, and then check that folder is in your $PATH https://classic.yarnpkg.com/en/docs/cli/global/

Yang Xu09:01:48

Hi, I need to write a method to complete data interaction when I invoke Clojure from Java. The detail of the problem is I need to convert the key and value of HashMap to the keyword, and HashMap has a hierarchy nested, maybe ArrayList, HashSet, or HashMap. I wrote the code below, but it didn’t work. So who can tell me how should I do?

(defn keywordize-kv [m]
  (println (let [f (fn [[k v]] [(if (string? k) (keyword k) k) (if (string? v) (keyword v) v)])]
             (walk/postwalk (fn [x] (cond
                                      (instance? java.util.HashMap x) (into {} (map f x))
                                      (instance? java.util.HashSet x) (into #{} (map f x))
                                      (instance? java.util.ArrayList x) (into [] (map f x))
                                      :else x))
                            (into {} m)))))

noisesmith17:01:31

postwalk simply does not work on types other than the standard clojure collections:

ins)user=> (def l (doto (java.util.ArrayList.) (.addAll ["a" "b"  "c"])))
#'user/l
(ins)user=> l
["a" "b" "c"]
(ins)user=> (clojure.walk/postwalk (fn [el] (if (string? el) (keyword el) el)) l)
["a" "b" "c"]

noisesmith17:01:46

it treats that list as a single thing it doesn't know how to process

noisesmith17:01:55

prewalk on the other hand can do the transform on the way down, which means it can navigate the structure

(ins)user=> (clojure.walk/prewalk (fn [el] (cond (instance? java.util.ArrayList el) (into [] el) (string? el) (keyword el) :else el)) l)
[:a :b :c]

noisesmith17:01:07

you shouldn't need that into call on the last line

noisesmith17:01:52

also,`f` will break on things that are not k/v pairs (things that are not elements of hash maps)

noisesmith17:01:20

also, if you check for the interfaces (`java.util.Map`, java.util.Set, java.util.List ) instead of the concrete classes, it will behave the same on your enumerated cases, but also be more general for other collection types

Yang Xu06:01:43

Wow, Thank you for so much advice, I change my code to below. It worked, and I think I should predefined API to implement data interaction of Java with Clojure.

(defn keywordize-kv [m]
  (let [f (fn [[k v]] [
                       (if (string? k) (keyword k) k)
                       (cond (string? v) (keyword v)
                             (instance? java.util.HashSet v) (into [] (map (fn [x] (if (string? x) (keyword x) x)) v))
                             (instance? java.util.ArrayList v) (into [] (map (fn [x] (if (string? x) (keyword x) x)) v))
                             )])]
    (walk/postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) (into {} m))))

caumond10:01:55

Hi, I find solutions but all really verbose and not idiomatic: I have an atom with a list of element, the list is not huge but will have some hundreds of elements.

(def i (atom (list {:a 3} {:a 2} {:a 5})))
(defn dequeue [atom-list-evt pred]
????)
(dequeue i (comp even? :a))
; --> {:a 2}
@i
;; -> '({:a 3} {:a 5})
I would like to write that dequeue function to return the first element in the list matching pred, and I expect that dequeue function to update the atom, dropping the element which has been returned.

raspasov10:01:01

@U018QDQGZ9Q a few points: 1. lists are not super idiomatic in Clojure 2. lists only support efficient adding/removing at the start of the list; that might not matter too much (like you said, a few hundred elements might be OK to recreate on every update if those updates are not too often; still that’s not a very idiomatic thing to do) 3. If you need to efficiently add/remove items based on index/key, perhaps you can consider sorted-map; this would allow you to maintain a queue-like feel with the sorted map’s keys

raspasov10:01:31

(def a (atom (sorted-map 2 {:a 5} 0 {:a 2} 1 {:a 3})))


(defn dequeue [a pred]
  (let [[k v] (some pred @a)]
    (when v
      (swap! a dissoc k)
      v)))

raspasov10:01:32

(dequeue a (fn [[k v]] (when (-> v :a even?) [k v])))

raspasov10:01:58

@a => {1 {:a 3}, 2 {:a 5}}

caumond11:01:31

Ok, I got it. I was confused because the key is "dynamic". Each element I drop will update the maps and their key. I need to think a little bit, but keeping the collection sorted with the sorted-map answer my question I guess. Thx

caumond11:01:19

But, in your solution, you "scan" the collection twice, one for "some", the other for dissoc, I understand that with sorted map it will be efficient. Aren't we able to do it in one raw? maybe something like split-by?

raspasov11:01:25

It’s true, we scan the collection in the (some … ) call

raspasov11:01:22

The (dissoc …) call is effectively constant speed (aka roughly O(1) ) so we can ignore that;

raspasov11:01:58

If you need constant scan… we’d need to devise something more clever; but I’d stick with this solution, unless high performance is needed for sure

caumond11:01:07

Yes, no doubt this simple solution is ok to start. Not sure where the design will lead me, so no need to optimize. I was just trying to understand how we are suppose to do.

raspasov11:01:59

Of course 🙂 “Constant scan” is a sort of a paradox by definition; you cannot really look at many items and expect that to be constant time… without the use of indices/full-text search, etc, etc (all solution involving many more moving parts)

raspasov11:01:48

One “clever” thing I’ve used in the past is the use of two maps: (def map-1 {:a 1, :b 2, :c 3})

raspasov12:01:10

(def map-2 {1 :a, 2 :b, 3 :c})

raspasov12:01:27

Basically two maps that are “inverse indices” of each other

raspasov12:01:56

That can help avoid scanning in some cases… if you’re able to lookup by exact value or key;

Daniel Stephens12:01:12

do you need it stateful as opposed to giving back both the popped value and the new seq with the value dropped?

Daniel Stephens12:01:24

something like this maybe, you could apply the same logic to your atom if you do need the state

(defn dequeue
  [pred coll]
  (let [head (take-while (complement pred) coll)
        [v & tail] (drop (count head) coll)]
    [v (concat head tail)]))

caumond14:01:27

if I understand well, this solution has an assumption that the collection is sorted, with values satisfying the predicate first.

Daniel Stephens14:01:18

(dequeue #(> % 5) (range 10))
=> [6 (0 1 2 3 4 5 7 8 9)]
nope, also nice and lazy (at least for elements after the first truthy value)
(first (dequeue #(> % 1000) (range)))
=> 1001

caumond14:01:22

ok ok, so sorry, I thought I was clojure fluent, ...

☺️ 3
noisesmith17:01:14

@ULNRSUK8C the problem with that function is it doesn't work with atom thread safety

noisesmith17:01:51

(but a version of it using compare-and-set! on an atom instead of swap! is possible)

caumond17:01:50

@U051SS2EU, why not compatible N

noisesmith17:01:17

because the value returned by swap! is the value held in the atom, and doing two different ops on an atom to provide one result isn't thread safe

Daniel Stephens18:01:10

@U051SS2EU nothing in that code snippet I posted was for an atom, however as far as I know it would be thread safe to do the following using swap! (which then uses compareAndSet underneath in a loop).

(defn dequeue
  [pred coll]
  (let [head (take-while (complement pred) coll)
        [v & tail] (drop (count head) coll)]
    [v (concat head tail)]))

(defn dequeue-atom
  [pred a]
  (-> (swap! a (fn [c] (let [[v next-c] (dequeue pred c)]
                         (vary-meta next-c assoc :popped v))))
      meta
      :popped))
but I failed to make the point I was trying to which was that unless an atom is really necessary, I'd avoid it entirely!

noisesmith18:01:45

I'm aware that swap! uses compareAndSet, but using clojure's compare-and-set! is less hacky than using metadata

(defn dequeue-atom
   [pred a]
   (let [v @a
         [res new-coll] (dequeue pred v)]
     (if (compare-and-set! a v new-coll)
       res
       (recur pred a))))

👍 3
noisesmith18:01:35

@ULNRSUK8C excellent point that it's better to avoid using an atom at all

caumond18:01:01

yes!! I thought it would be the first answer on slack: don't do atoms !!

caumond18:01:14

as few as possible

✔️ 3
caumond18:01:53

but this atom will replace my database... the persistance until I push it on a real database

Christian11:01:07

Is a library the same as a DSL? I don't really get the difference. I mean the implementation in Clojure.

Lennart Buit11:01:48

A DSL is a domain specific language, meaning that it is a sublanguage for a more specific purpose. A good example may be SQL, being a DSL for interacting with databases

Lennart Buit11:01:48

That said, you can embed a DSL inside another language, for example HoneySQL is a version of the SQL DSL that used clojure data structures as its medium. So the line gets more blurry

raspasov11:01:43

Excluding things like HoneySQL, which can be described as more or less as 1-to-1 conversion between Clojure data and SQL, when people say DSL in Clojure, they usually mean some considerable use of macros; that’s usually frowned upon, unless a really good reason for the DSL/macro use exists

raspasov11:01:38

HoneySQL is great, I think it’s a must use if you need to deal with a SQL database from Clojure

Christian12:01:52

With the "if you can use a function, don't use a macro" concept, I was wondering if something like HoneySQL is just a bunch of functions (with java interop or something) with the occasional macro. DSL being "helps to deal with sql databases". While any library would be "helps to deal with xy"

raspasov12:01:28

Right… the definition of DSL is up for interpretation; at least when I hear it, I usually associate it with macros; and macros are rarely used in majority of Clojure libraries, likely for good reasons

raspasov12:01:57

The (go…) macro from core.async being a big exception

noisesmith17:01:56

many clojure dsls use raw data instead of macros - eg. the hiccup dsl

noisesmith17:01:40

another example of a common DSL is regex, which clojure has a reader for but is usually indistinguishable from a string

noisesmith17:01:31

for binding bodies are a DSL (thanks to :while / :when / :let syntaxes)

noisesmith17:01:48

I think the key is that most libraries use the language's existing syntax and semantics to solve domain problems DSLs on the other hand introduce a new syntax and/or a new semantics (hiccup reuses clojure's existing syntax but impose a new semantics, for / doseq introduce a new syntax, regex has special reader rules) - they become a new language you need to learn in some meaningful way (even if it's only a couple of rules), and they make the implicit promise that your problem is easier to solve in that language.

🙌 3
noisesmith17:01:59

core.match and core.logic are good examples of DSLs for clojure - the code written with those libraries isn't readable / intelligible until you learn the DSL used

noisesmith17:01:43

in my work experience both macro based and data structure based DSLs are looked at with some skepticism - they introduce a barrier to understanding the code, and sometimes they require more work than the convenience they offer (especially with larger teams)

👆 3
noisesmith17:01:12

even regex tends to be minimized / simplified and replaced with more normal looking building blocks

noisesmith18:01:47

and how's this for meta: in the mathematical vocabulary, each regex individually describes a language

🤯 3
Christian07:01:15

That wouldn't be confusig at all. Imagine you want to understand a bit of regex and somebody starts by introducing a nondeterministic finite automaton first, because that's basically the same thing 😄

Lennart Buit10:01:16

And then starts to try to explain to you that programming regexes are strictly more expressive 😛 You know, because perl

Piotr Brzeziński12:01:01

Hello 🙂. Is there any example I could view of using speech synthesis in clojurescript? I’ve been trying to follow this https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis but I’m having troubles understanding how to interop with JS classes correctly.

simongray12:01:39

I think you will have more luck trying to find more general examples of interop. What are you having trouble with?

Piotr Brzeziński12:01:57

So there`s this part

var utterThis = new SpeechSynthesisUtterance(inputTxt.value);
that then has to be mutated in JS like so
utterThis.pitch = pitch.value;
utterThis.rate = rate.value;
so then I can call
synth.speak(utterThis);
I know I can setup the synth with
(def synth (.-speechSynthesis js/window))
but I fail to understand the utterThispart and then how to call synth.speak(uttherThis)

simongray12:01:19

(def utterThis (new js/SpeechSynthesisUtterance "some text"))
or
(def utterThis (js/SpeechSynthesisUtterance. "some text"))

simongray12:01:08

(.speak synth utterThis)
to call it

Piotr Brzeziński12:01:29

But the speak method exists on synth

Piotr Brzeziński12:01:32

This is the part I dont get

Piotr Brzeziński12:01:50

(-> utterThis .-speak .synth) like that?

simongray12:01:52

when you call a method the first argument is always the object containing the method.

simongray12:01:18

So the second argument of the call would the first (and only) arg in the JS version.

Piotr Brzeziński12:01:23

Right, makes sense. So then I get back a method and apply utterThis to it. Nice 🙂

simongray12:01:43

np! looks like a fun API. Will need to check it out.

Piotr Brzeziński12:01:03

Yeah, I want to use it for a simple metronome app.

simongray12:01:43

Here’s a full example

(let [utterThis (js/SpeechSynthesisUtterance. "some text")]
  (js/window.speechSynthesis.speak utterThis))

Piotr Brzeziński12:01:10

Thanks, that helped a lot!

🙏 3
simongray12:01:41

note that you can use dot as a shortcut directly for both properties and functions, e.g. you can use (js/window.speechSynthesis.speak utterThis) directly so that you in princicple never have to define synth.

👍 3
mloughlin12:01:49

I've got a bunch of decimal numbers that are represented as vecs of two ints [10 0] => 10.0 . What's the least ridiculous way of adding them together? e.g. (my-add [9 9] [1 2]) ; => [11 1]

simongray12:01:35

(defn my-add [& tuples]
  (let [tuple->float (fn [[n1 n2]] (Double/parseDouble (str n1 "." n2)))]
    (reduce + (map tuple->float tuples))))

Daniel Stephens13:01:32

do you need to think about negatives? is [-10 5] equal to -9.5 or -10.5, maybe:

(fn add [[t1 d1] [t2 d2]]
  (let [D (+ d1 d2)
        d (rem D 10)
        t (+ t1 t2 (int (/ D 10)))]
    [t d]))
or
(fn add [[t1 d1] [t2 d2]]
  (let [a (+ t1 t2 (/ d1 10) (/ d2 10))]
    [(quot a 1) (* 10 (rem a 1))]))

mloughlin14:01:48

Thanks both, that's given me a something to think about! I don't need to bother with negatives, it's a business rule where the numbers are between 0.0 and 10.0 and max 1 decimal place after the point.

raspasov05:01:46

(defn my-add [[n1 d1] [n2 d2]]
  (let [d3 (+ d1 d2)
        [-n d3'] (cond
                   (< d3 10) [0 d3]
                   (= d3 10) [1 0]
                   (< 10 d3) [1 (- d3 10)])]
    [(+ n1 n2 -n) d3']))
Using some elementary school math rules 🙂 (expressed within the cond)

Ilmari Pohjola13:01:30

Does anyone know if I need to use Compojure in HerokuApp? I have a very basic "application" in Heroku, which crashes. I'll add the simple code to comments. Error trace shows next lines, which I believe asre important. Picked up JAVA_TOOL_OPTIONS: -Xmx300m -Xss512k -XX:CICompilerCount=2 -Dfile.encoding=UTF-8 2021-01-24T12:54:12.831812+00:00 app[web.1]: Syntax error (ClassNotFoundException) compiling at (ring/util/servlet.clj:1:1). 2021-01-24T12:54:12.831836+00:00 app[web.1]: javax.servlet.AsyncContext

Ilmari Pohjola13:01:29

Project.clj: (defproject mutable-app "1.0.0-SNAPSHOT"   `:description "FIXME: write description"`   `:url "http://mutable-app.herokuapp.com"`   `:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"`             `:url "https://www.eclipse.org/legal/epl-2.0/"}`   `:dependencies [[org.clojure/clojure "1.10.1"]`                  `[ring "1.8.1"]`                  `[metosin/reitit "0.4.2"]`                  `[metosin/ring-http-response "0.9.1"]`                  `[ring/ring-jetty-adapter "1.2.2"]`                  `[ring/ring-devel "1.2.2"]`                  `[environ "0.5.0"]]`   `:min-lein-version "2.0.0"`   `:plugins [[environ/environ.lein "0.2.1"]]`   `:uberjar-name "mutable-app-standalone.jar"`   `:profiles {`       `:production {:env {:production true}`        `:uberjar {:aot :all}}})`

Ilmari Pohjola13:01:49

Procfile: web: java $JVM_OPTS -cp target/mutable-app-standalone.jar clojure.main -m mutable-app.web

Ilmari Pohjola13:01:53

Then the following two code files: (ns endpoints (:require [ring.util.http-response :as response])) (def addition ["/addition" {:get (fn [request] (let [params (:query-params request) x (Long/parseLong (get params "x")) y (Long/parseLong (get params "y"))] (response/ok {:total (+ x y)})))}]) (def math-routes ["/math" addition])

(ns mutable-app.web
  (:require
   [ring.adapter.jetty :as jetty]
   [environ.core :refer [env]]
   [ring.middleware.params :as params]
   [reitit.ring.middleware.muuntaja :as muuntaja]
   [muuntaja.core :as m]
   [reitit.ring.coercion :as coercion]
   [reitit.ring :as ring]
   [endpoints]))


(def app
  (ring/ring-handler
   (ring/router
    [endpoints/math-routes]
    {:data {:muuntaja   m/instance
            :middleware [params/wrap-params
                         muuntaja/format-middleware
                         coercion/coerce-exceptions-middleware
                         coercion/coerce-request-middleware
                         coercion/coerce-response-middleware]}})
   (ring/create-default-handler)))

(defn -main [& [port]]
  (let [port (Integer. (or port (env :port) 5000))]
    (jetty/run-jetty #'app {:port  port
                            :join? false})))

Hagenek13:01:25

Why does this loop not terminate?

(defn sing
  "Given a start and an optional end, returns all verses in this interval. If
  end is not given, the whole song from start is sung."
  ([start]
   (loop [start_num start]
     (when (> start_num 0)
       (println (verse start_num)))
     (recur (- start_num 1))))
  ([start end] (str "You start at " start " and end at " end)))

Max Deineko13:01:38

If it does not terminate then possibly because recur is called unconditionally inside loop

motform13:01:44

I have not run it, but I belive you are missing any kind of terminating condition. The body of the loop containts a conditinal println with when, after which you recur. Thus, you will always recur. If you want to terimnate, you might want to use an if. Also, if you are looping over a number to perform side effects, it is common to use dotimes. https://clojuredocs.org/clojure.core/dotimes

tvirolai13:01:51

Yep, check your parentheses. Also, as a linter would probably notify, check out the pos? and dec functions

tws15:01:35

I can tell you’re doing the exercism-clojure problems. this is completely unreadable, but I really liked my golfing solution to beer-song:

(defn verse [count]
  (let [next-count (if (zero? count) 99 (dec count))]
    (cl-format nil "~[No more~:;~:*~D~] bottle~:P of beer on the wall, ~
                    ~:*~[no more~:;~:*~D~] bottle~:P of beer.~%~
                    ~:*~[Go to the store and buy some more~:;Take ~:*~[~;it~:;one~] down and pass it around~], ~
                    ~[no more~:;~:*~D~] bottle~:P of beer on the wall.~%"
               count next-count)))

(defn sing
  ([from] (sing from 0))
  ([from to] (join "\n" (map verse (range from (dec to) -1)))))

clojure-spin 6
Hagenek17:01:35

Wow! That's pretty crazy! I ended up with this: (_defn_ sing   `"Given a start and an optional end, returns all verses in this interval. If`   `end is not given, the whole song from start is sung."`   `([start]`    `(s/join (add-newline (reverse (map verse (range (inc start)))))))`   `([start end] (s/join (add-newline (reverse (map verse (range end (inc start))))))))`

tws17:01:26

it is common practice to have your [1-arity with default] version call your other one with the default, avoiding the duplication in there

tws17:01:00

like i did in the

(defn sing
  ([from] (sing from 0))
...

3
Hagenek17:01:06

Yeah, I saw some other solutions after delivering, and have already fixed that! 😃 Thanks

tws17:01:12

and yes, cl-format is super powerful and arcane

Hagenek17:01:27

cl-format looks like wizardry alright

tws17:01:38

pages of docs

Dave Russell15:01:54

Is it possible to have spec check the validity of non-namespaced keywords that are not declared in an s/keys map? For example, you can define:

(s/def ::foo int?)
(s/def ::myspec (s/keys))
Such that (s/valid? ::myspec {::foo :bad-non-int}) will fail. But I want it to fail even for the non-namespace keyword {:foo :bad-non-int}

alexmiller15:01:55

no, not like that

alexmiller15:01:38

I mean, you can use (s/keys :opt-un [::foo])

Dave Russell15:01:39

Then I would have to specify it 🙂 But I guess maybe there's no sane way to auto-namespace keywords that aren't explicitly specified via :req-un or :opt-un

Dave Russell15:01:09

The use-case is that I'm using specs to auto-generate swagger docs and wanted to hide a keyword by just removing it from the spec. I'll opt to post-process the generated swagger instead. Thanks!

Hagenek15:01:01

When working with the clojure test library, is it possible to get it to give you the explicit values it is comparing against? I get this, which is not very useful for finding small errors in strings: FAIL in (test-song) (beer_song_test.clj:47) expected: (= song-3-0 (beer-song/sing 3)) actual: (not (= "3 bottles of beer on the wall, 3 bottles of beer.\nTake one down and pass it around...))

dpsutton15:01:26

user=> (t/deftest foo (t/is (= "bob" "rob")))
#'user/foo
user=> (foo)

FAIL in (foo) (NO_SOURCE_FILE:1)
expected: (= "bob" "rob")
  actual: (not (= "bob" "rob"))
nil
user=>

👍 6
dpsutton15:01:48

"bob" is the expected and it was given "rob". (not (= "bob" "rob"))

Hagenek16:01:16

Thanks a lot! 😃

dpsutton15:01:11

isn't that what the actual: line is doing?

Hagenek16:01:05

I guess I am looking for it to print expected: "3 bottles of whiskey on the wall...." actual: "3 bottles of beer on the wall..."

mloughlin17:01:34

Is there a function in core that lets me call distinct but with a predicate or similar? I have a sequence of maps where some are identical except for one value (of the same key each time). I want to discard them when the rest of their values are the same.

dpsutton17:01:52

there's a library called medley which has a distinct-by function. You could grab that dep or perhaps just copy and attribute the code for that particular function if license allows

3
thanks2 3
ho0man17:01:48

Hi everyone, I'm having problem transforming java objects to clojure maps. Where normally calling the bean function would work, I'm getting an empty map :

(-> consumer-record clojure.java.data/from-java) => {}
  (-> consumer-record bean keys) => {:class org.apache.kafka.clients.consumer.ConsumerRecord} 
Calling .key and .value on the consumer record returns the desired data. Also is there a more performant way of transforming such java objects to clojure maps ? thanks a lot in advance

Daniel Stephens18:01:31

ConsumerRecord's aren't beans so the default reflection based things won't work. from-java is super helpful here as it's a defmulti that dispatches on class type, so you can just extend it with your custom conversion, e.g.

(defmethod clojure.java.data/from-java ConsumerRecord
  [^ConsumerRecord cr]
  {:topic (.topic cr)
   :key (.key cr)
   :value (.value cr)})
=> #object[clojure.lang.MultiFn 0x7506874e "[email protected]"]
(clojure.java.data/from-java (ConsumerRecord. "topic" 0 0 "key" "value"))
=> {:topic "topic", :key "key", :value "value"}

Daniel Stephens18:01:29

this is probably close to as performant as you'll get as well, since you can tell clojure the types it's dealing with so it can avoid any reflection (afaik)

NPException18:01:38

Is from-java a function that already exists somewhere in Clojure? It doesn't seem to be part of the clojure.core namespace.

NPException18:01:09

Ignore me. I should have read your code example in detail. 😄

ho0man18:01:43

Thanks a lot @ULNRSUK8C

🎉 3
bschup18:01:52

Just getting started with VSCode clover extension, socket repl, and reveal. I can evaluate code in the editor and see an appropriate result in the clover repl tab, I expect to see similar output in external reveal window but no. I do however see output in the reveal window when evaluating something similar to (tap> (+ 2 2)).

Joe18:01:59

I don’t know if this is the ‘right’ way, but I set up a custom shortcut to tap block. Or actually I copied Sean Corfield’s shortcuts, which are on GitHub.

bschup19:01:30

Ah, OK thanks for the tip, will take a look at Sean's key bindings.

seancorfield19:01:53

@UMPB4KLBV Yup, you need my key bindings (and config file too).

seancorfield19:01:18

If you get stuck, ask in #chlorine (Clover is the "same" as Chlorine).

bschup19:01:48

Yeah that did the trick, thanks!

GGfpc19:01:10

Hello! I'm trying to convert a piece of code from clj-http to clj-ajax because I want to use it in a cljs file. However I'm struggling to get the same result This clj-http code was this

(defn handle
  [request]
  (println (first request)))(defn get-game-data
                              "Fetches the game data from the Lichess API"
                              [user-id]
                              (let [url (str BASE_URL user-id)
                                    request-headers {:headers      {:accept "application/x-ndjson"}
                                                     :query-params {:max 500}}]
                                (-> (client/get url request-headers))))
And this is the cljs-ajax
(defn handle
  [request]
  (println request))

(defn get-game-data
  "Fetches the game data from the Lichess API"
  [user-id]
  (let [url (str BASE_URL user-id)
        request {:headers {"Accept" "application/x-ndjson"}
                 :params  {:max 500}
                 :handler handle}]
    (GET url request)))
The thing with the latter is that the handle function only prints
#object[org.apache.http.nio.entity.ContentInputStream 0x1732b20d [email protected]]
Not sure how to proceed

Daniel Stephens21:01:44

Not familiar with cljs so may well have missed the point here but have you tried (println (slurp request))

noisesmith19:01:46

how is cljs returning an apache http nio?

noisesmith19:01:38

anyway, you should expect an AJAX method to return a js promise, or allow a callback to be called when the request completes, this is needed because js is single threaded and otherwise you would freeze the page

noisesmith19:01:24

I've only used AJAX directly via interop, providing a callback or using the promise it returns is relatively straightforward

GGfpc19:01:35

Yes, the handle function is the callback, it's the handle function that prints the ContentInputStream that is passed to it

noisesmith19:01:35

if that's actually cljs code, my first guess is that your request handler on the server is broken, and your serializer is "doing its best"

noisesmith19:01:26

because I have no idea how a java class would show up in the browser

GGfpc20:01:31

I don't control the server, but if helps this is a cljc file and I'm running it in a cljs nrepl using Cursive

noisesmith20:01:55

if the lib is cljc compatible, the simplest explanation is that the repl running this code is actually clj (try doing something cljs specific like (js/console.log "foo"))

Scott Starkey21:01:39

Hi folks - I have a beautiful program that uses Java interop — it allows a user to select a csv file, and outputs an newly processed file. Works in my Doom Emacs REPL. I made a Lein uberjar of it, and it works on my dev machine (Mac). I have tested the Jar file on my other machine, a Windows machine, and program death. (“Well it’s Windows. There’s your problem right there!“)

c:\test>java -jar filter-program-0.1.0-SNAPSHOT-standalone.jar
Exception in thread "main" java.lang.IndexOutOfBoundsException: Invalid index
        at javax.swing.DefaultRowSorter.convertRowIndexToModel(Unknown Source) 
[20 lines of error codes deleted.]
Any thoughts why an uberjar would work one place and not another?

manutter5121:01:23

Different version of Java installed on the Windows machine?

Scott Starkey21:01:40

Hmmm… Let me double check versions.

Mno21:01:10

I would’ve guessed that as well.. since that’s the JVM promise

Scott Starkey21:01:14

Mac: java version “1.8.0_201” Windows: java version “1.8.0_221"

Mno21:01:06

That gap doesn’t seem big enough to cause it..

Scott Starkey21:01:42

Should I update my Mac environment, just to be sure?

Mno21:01:30

Same CSV? Also I’ve had issues in the past where the parsers take into account the locale of the computer they’re running on.. (and prefer : over , or even TAB )

Mno21:01:32

I really doubt it could be that minor version, but if you’ve ruled out everything else, might as well try?

Scott Starkey21:01:35

Same CSV, but on the Windows machine it never gets as far as that. It displays the window (frame) but dies before it pulls up the dialog box for the user.

Mno21:01:54

Very strange… hopefully someone smarter/more experienced has more ideas.

❤️ 3
noisesmith21:01:24

could this be somehow related to windows character encoding config, leading to strings of the wrong length then leading via some set of dominos to looking for a column in the UI that doesn't exist?

Scott Starkey21:01:11

Here’s the exception thread, if that would be helpful.

seancorfield21:01:35

@scotto Do you do any filesystem path manipulation in your code? Windows uses C:\path\like\this but macOS/Linux are /path/like/this. Or perhaps you assume \n is end of line rather than potentially \r\n on Windows?

noisesmith21:01:38

what's going on around like 136 of your file?

seancorfield21:01:53

That was going to be my next question 🙂

Scott Starkey21:01:52

Line 136:

(.setCurrentDirectory fc (File. (System/getProperty "user.home")))

Scott Starkey21:01:10

Thanks Daniel - I’ll look into that!

🤞 3
seancorfield21:01:00

Looks like Clojure needs to use reflection on that line -- and that is somehow wired up to some Swing UI?

Scott Starkey21:01:26

fc is (def fc (JFileChooser.))

Nassin21:01:08

are you passing in the same CSV file in both cases?

Nassin21:01:15

looks like a programming error

Scott Starkey21:01:45

I create a new output file to write to. I use Clojure Data CSV, using almost the exact format of their lazy reader/writer example. https://github.com/clojure/data.csv/

manutter5121:01:05

Try editing line 136 to get the user.home property first, print it out, and then use it to set the current directory, maybe that’ll tell you something.

👍 3
alexmiller22:01:17

it's possible for user.home to be null

alexmiller22:01:52

if it is, that makes that File. constructor a reflective, unresolveable call

alexmiller22:01:06

although that doesn't match that stack trace

borkdude22:01:15

can't repro that:

user=> (defn bar [] (java.io.File. (System/getProperty "user.home")))
#'user/bar
user=> (bar)
#object[java.io.File 0x48535004 "/Users/borkdude"]
user=> (defn bar [] (java.io.File. (System/getProperty "user.homex")))
#'user/bar
user=> (bar)
Execution error (NullPointerException) at java.io.File/<init> (File.java:278).
null

sova-soars-the-sora23:01:16

Hi I'm using a wrapper over google translate... it works great but I'm not sure if I can hide a warning that google translate produces each time:

com.google.cloud.translate.TranslateOptions <init>
WARNING: Ignoring Application Default Credentials GOOGLE_APPLICATION_CREDENTIALS: using explicit setting for API key instead.

noisesmith00:01:20

the good news is that it's possible to set message levels on a per-package basis, the bad news is that java logging config is overly complex

sova-soars-the-sora00:01:41

haha awesome. well that's good to know. I see this Logger.LEVEL stuff ... blargh

noisesmith00:01:25

yeah, you can even say "no warnings from com.google.cloud specifically"

noisesmith00:01:13

the annoying part is there's multiple ways to say that, and which way you do it depends on your app because there are parallel and incompatible ways to configure

noisesmith00:01:31

and you can get changes in which system is used caused by config files in your deps for example

sova-soars-the-sora00:01:19

right on.. well maybe a hacky way to delete that message from the result will be fine. I washoping I could disable this logging on the google cloud console somewhere but it's complex and made for robots

sova-soars-the-sora02:01:37

How would I go about saying I don't want any warnings from com.google.cloud?

sova-soars-the-sora02:01:08

;/ doesn't seem to do it

noisesmith02:01:14

it depends on which logging system you decide you are using, and which style of config file it uses (this is a weird mess because of the hyper-flexible way various java logging systems and compatibility layers are defined) - search for "architecture" here to see a diagram https://stackify.com/logging-java/

sova-soars-the-sora02:01:13

okay... thanks, i will do some learning. I really just want to suppress the WARNING: x on cloud translate

sova-soars-the-sora02:01:28

since i plan on doing lots of translate calls and don't want the repl blasted

noisesmith02:01:30

if only that were as simple as it should be

sova-soars-the-sora02:01:49

commence the weeping

sova-soars-the-sora02:01:38

theoretically could i set the warning "status" to something other than warn

sova-soars-the-sora02:01:45

and then see no more warnings?

sova-soars-the-sora02:01:11

pardon my ignorance i know very little about this fiasco xD

noisesmith02:01:09

all of the loggers have a concept of a "log level", which can be modified (either globally or on a per package basis) to suppress messages below a desired level

noisesmith02:01:49

warn, debug, error, info etc. have numeric constants and the normal style of config is to set a numeric level (via its symbol)

noisesmith02:01:20

but many of the logging systems allow things that are more targeted than that (eg. per package config if someone else made their stuff too verbose / not verbose enough)

sova-soars-the-sora02:01:21

How would one hide the warnings from a specific namespace or package?

sova-soars-the-sora04:01:54

welp. I used their recommended export G_TRANS_CREDENTIALS thing on the command line instead of manually passing in an api key and... the warnings are gone

sova-soars-the-sora04:01:00

but like... why google why

noisesmith17:01:02

> How would one hide the warnings from a specific namespace or package? this is possible with multiple tools, but the universal answer is "via config" - you actually have to sort out which logging backend + adaptors work for your code plus the deps you pull in (I wish I had a satisfying universal answer, but it's just been a "find the dirty hack that works with this codebase" kind of thing)