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/

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, ...

☺️ 1
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))))

👍 1
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

✔️ 1
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.

🙌 1
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)

👆 1
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

🤯 1
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

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 2
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))
...

1
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=>

👍 2
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

1
thanks2 1
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

🎉 1
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.

❤️ 1
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!

🤞 1
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.

👍 1
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