Fork me on GitHub
#beginners
<
2020-08-19
>
Ben Sless07:08:45

Hi all, should this happen?

(if (Boolean. "false") "true" "false") 
;; => "true"
(= (Boolean. "false") false)
;; => true

noisesmith07:08:20

@ben.sless never use the Boolean constructor

noisesmith07:08:39

unlike java, clojure's if uses an identity check on Boolean/FALSE rather than checking the value

noisesmith07:08:06

you can use Boolean/valueOf in place of the constructor to get the correct result

noisesmith07:08:57

(or wrap the Boolean constructor call in a call to clojure.core/boolean, but valueOf is more direct)

Ben Sless07:08:28

Thanks @noisesmith, don't worry, I'm not using it 🙂

Ben Sless07:08:45

A colleague asked me that question and I realized I had no idea, so I came here to ask the experts

noisesmith07:08:43

I probably don't need to go around using the language of a priest about this - of course I mean that there's no case I know of where using the Boolean constructor makes sense in clojure code - there's always a simpler and more correct solution

noisesmith07:08:19

counter examples or proof of my statement both welcome haha

Ben Sless07:08:27

Every rule as an exception, but it may take a while to find it

noisesmith07:08:43

now we have the pragmatic choice - which wastes more time, looking for a constructive use of the Boolean constructor in clojure code or short circuit assuming that it's never valid 😆

Ben Sless07:08:37

Life needs an element of mystery, I think. Label it under "There Be Dragons" until a brave soul ventures out and returns with a useful answer 😄

ska10:08:31

(true? (Boolean. "false"))
;; => false

ska10:08:55

But the documentation of if comes to the rescue:

if
 (if test then else?)
Special Form
  Evaluates test. If not the singular values nil or false,
  evaluates and yields then, otherwise, evaluates and yields else. If
  else is not supplied it defaults to nil.
The constructed object may be = to false, it prints as false, but it's not the "singular value[...] false". Intuitiv? Probably not. Relevant for real life? Neither, I'd say. At least, I've not hit that problem in the past 10+ years.

andy.fingerhut13:08:35

One way in which I think it is relevant for real life is that I am fairly sure that it improves the performance of Clojure code execution for if to behave as it does, versus also taking the false branch for other values = to false

Brandon Stubbs12:08:15

Hey, I had a question on destructuring:

(let [{:keys [region]} {:region "x"}]
    region)
Could I bind region to another name, for instance src-region

rpkarlsson12:08:26

Perhaps:

(let [{src-region :region} {:region "x"}]
    src-region)
?

Brandon Stubbs12:08:17

Oh yes great thank you exactly what I was after 😄

👍 1
zackteo12:08:32

Hi guys, what version of openjdk should I be running? Is there any reason for me not to go from openjdk 8 to 11 ? 😮

practicalli_john15:08:52

Java 11 is a very safe choice for running Clojure. It's been around long enough that any bugs have been fixed. Make sure your build tools are up to date and of course test your apps before changing Java versions in production

zackteo12:08:32

I understand that both are LTS versions and from state of clojure https://clojure.org/news/2020/02/20/state-of-clojure-2020 ... > Clojure itself has been using Java 8 as the baseline JVM for a couple years and will continue to do so (while also supporting newer versions of Java). When running Clojure, we recommend Java 8 or 11 right now. So in that sense is perfectly good for me to switch to java 11?

jumar18:08:33

I’ve been running in dev with JDK 14 even and it works well; JDK 11 in prod for a while and no issues. Note that LTS is a concept that only makes sense when talking about particular distributions (in particular Oracle’s Jdk) and their guarantees - you cannot say JDK11 in general is lts

dharrigan12:08:49

I use Clojure with JVM 11 in production. Zero issues.

dakra12:08:01

Is there an easy way to use deps.edn :main-opts where I can say where exactly the command line argument from the user goes? E.g. I have this in my deps.edn aliases

:uberjar {:extra-deps {seancorfield/depstar {:mvn/version "1.0.97"}}
            :main-opts ["-m" "hf.depstar.uberjar" ...USER-ARG-HERE... "-C" "-m" "myapp.core"]}
Then I would just like to call something like clojure -A:uberjar myappUberJar.jar

seancorfield16:08:28

I guess it would be a good enhancement to allow the JAR file to be specified in any order with the options. Can you create an issue on my depstar repo on GitHub about that so I don't forget?

seancorfield16:08:38

@daniel415 Also, what did you think of Alex's suggestion for a -X entry point? I'm not sure that would be easier for users but it is another option to consider.

dakra16:08:50

I think the map entry point is probably a good idea in general but for my use-case where I want that the user has to type as little as possible nothing beats -A:uberjar xxx.jar I guess.

seancorfield18:08:06

Yup. Working on it right now.

seancorfield19:08:54

@daniel415 that is implemented on *develop* if you want to depend on depstar via :git/url and :sha to test it out.

seancorfield20:08:35

It will accept the JAR file name and options in any order now. You can also specify the JAR file name with -J or --jar if you want 🙂

dakra21:08:15

That was fast 🙂 I just tested it

:uberjar {:extra-deps {seancorfield/depstar {:git/url ""
                                               :sha "c461ba830e069edf0f011169c18595a4a8f917fc"}}
            :main-opts ["-m" "hf.depstar.uberjar" "-C" "-m" "myapp"]}
and now I can just do the clojure -A:uberjar myapp.jar like I wanted. Thanks for the fast help.

seancorfield21:08:10

Thank you for testing that and confirming it works!

alexmiller13:08:47

no, that's not an available feature

dakra13:08:51

ok, thanks.. I guess I'll just write in the readme that the user has to append the -C and -m options themself 😉

dharrigan13:08:51

I think #babashka has some sort of uberjar'ing facility...a recent addition I think

dakra13:08:33

Nice. babashka is really cool. But I need a "real" java uberjar und not a bb uberjar. Very nice nevertheless 🙂

borkdude13:08:16

Maybe you could ask @seancorfield to make the uberjar name itself also a normal CLI arg

borkdude13:08:29

With backwards compatibility of course

alexmiller13:08:35

better, I would suggest a map entry point compatible with -X and overrides

borkdude14:08:13

@daniel415 Creating an uberjar with babashka that's runnable with Clojure:

[email protected] /tmp $ mkdir -p uberjar/src
[email protected] /tmp $ cd uberjar
[email protected] /tmp/uberjar $ echo "(ns foo (:gen-class)) (defn -main [& args] (prn :hello))" > src/foo.clj
[email protected] /tmp/uberjar $ clojure -m foo
:hello
[email protected] /tmp/uberjar $ mkdir -p classes
[email protected] /tmp/uberjar $ clojure -e "(require 'foo) (compile 'foo)"
foo
[email protected] /tmp/uberjar $ ls classes
foo$_main.class                      foo$fn__153.class                    foo$loading__6721__auto____151.class foo.class                            foo__init.class
[email protected] /tmp/uberjar $ bb -cp $(clojure -Spath):classes -m foo --uberjar /tmp/foo.jar
Building uberjar: /tmp/foo.jar
[email protected] /tmp/uberjar $ ( cd /tmp; java -jar foo.jar )
:hello

🙌 1
borkdude14:08:30

The crux: you need to compile the main class yourself with Clojure, since babashka doesn't do compilation

borkdude14:08:39

Note: I've just merged the uberjar feature to master, it's not released as of now, but there are pre-release binaries in #babashka_circleci_builds

David Pham15:08:11

When we have an atom and we add a watch to it, what happens to the watch if the atom gets garbage collected?

alexmiller15:08:31

it gets garbage collected too

alexmiller15:08:56

watches are not active threads, they are side effects triggered on change

David Pham15:08:54

even in ClojureScript?

noisesmith16:08:07

especially in cljs, since there's no extra threads there

David Pham16:08:30

Thanks a lot!

Santiago19:08:39

I’m new to working with atoms. I need one, because I need to load and unload models from memory in my API (https://github.com/jcpsantiago/sagemaker-multimodel-clj). If a model is already in memory the API needs to reply with a 409 — this means I need to check if the model is already in the atom. If it’s not, my fun must load it. How do I avoid derefing the atom (`current_model`) to test if model-name is loaded, and then swap! ing? The code below makes it possible for the state to change between the if and the swapping, which is far from ideal.

(defn load-model!
  [model-name url current-models]
  (if (db/loaded? model-name @current-models)
    (do
      (println (str "Model " model-name " is already loaded!"))
      {:status 409
       :body ""})

    (let [xgbmodel (xgb/load-model (str url "/model.xgb"))]
      (swap! current-models conj 
             {model-name 
              {:model-name model-name :model-url url :model xgbmodel}}))))

jsn21:08:44

Atom doesn't seem to be a problem here. What do you want to happen if the second load-model! is called while the first call is being executed?

Santiago05:08:14

@UTQEPUEH4 from my understanding the following would happen: request1 checks the atom, model is not loaded so proceeds to swap!; request2 attempts to deref the atom and waits until request1 finished the swap!. I think the issue arises when both requests try to check if the model is loaded, see that it is not and then proceeded to load it -- now there will be two loading attempts. Even though the final result is the same when you look at the atom, the second request should respond with a 409 instead of 200. Am I thinking correctly? This may also all be moot I don't know :man-shrugging:

jsn08:08:38

If you want the second request to return 409 and not to start loading the model, then you should atomically mark the model-name as "loading" before you start loading the model

jsn08:08:19

you can do it using e.g. swap-vals

jsn08:08:13

I'm curious btw, why do you want the second request to return 409? Why don't you want your requests to be idempotent?

Santiago11:08:20

I need to abide by AWS's contract which says that if the model is already loaded into memory the server should reply with 409. It seems letting two requests try to load the model in a race condition would still be fine because the end effect is still the same. Adding the fact it's an edge case, I shouldn't worry too much. Is that correct? Thanks for swap-vaks though, I'll check it out

jsn11:08:56

I don't know if the race condition will be tolerated; perhaps it will, but I'd still avoid it -- it's nicer that way, and should be easy, AFAICS

jsn12:08:23

I can't help but think that the following structure would be a good match: an atom hash (by name) of future -s

Santiago17:08:45

@UTQEPUEH4 could you elaborate? I don’t know what you mean with an atom hash 😅

jsn18:08:53

(def loaded-models (atom {}))

Santiago20:08:30

ah yes that’s what current-models is, it’s just passed to that function