clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
zackteo 2021-01-25T02:43:36.260400Z

Hello! I have a uni project to do a distributed app service (e.g. a distributed lock server) - with emphasis on the service A friend of mine approached me to perhaps take this chance to learn Clojure - but I'm not sure how to evaluate the feasibility of doing so in Clojure vs say Go. Not sure if anyone is able to offer any advice before I look into it further šŸ˜® The requirements are as follows: - Maintain correctness of the application w/o faults - Should provide scalable service (#nodes supported) - Fault tolerance - Byzantine fault tolerance (Bonus) - Implement a suitable consistency model

Nassin 2021-01-25T04:12:27.262100Z

There's way more literature and implementations about all of that on the Internet in Go for sure, and IMO, this in area where Go is used the most, low-level distributed network services

Nassin 2021-01-25T04:14:04.262800Z

So depends more on if you value community/ecosystem resources.

Nassin 2021-01-25T04:16:41.264Z

There's lots resources for Java too I guess, the Clojure world just reuses the Java implementations.

zackteo 2021-01-25T04:55:44.265800Z

Thanks @kaxaw75836, I think it was too tough to find more members who would do clojure anyway. Maybe something to explore, but not within my academics i guess šŸ™‚

zendevil 2021-01-25T06:11:26.266800Z

Iā€™m trying to upload a video to the server as follows:

(reg-event-fx
 :shoot-video
 (fn [coeffects [_ !camera]]
   (if (-> coeffects :db :shoot-video)
     (do
       (prn "stopping shoot video")
       (. @!camera stopRecording)
       )
     (go
       (prn "starting shoot!")
       (let [uri (.-uri (<p! (. @!camera recordAsync)))]
         (. MediaLibrary saveToLibraryAsync uri)
         (prn "dispatching with this uri" uri)
         (dispatch [:save-uri uri])
         (dispatch [:upload-shot-video]))))
   {:db (assoc (:db coeffects) :shoot-video (not (:shoot-video (:db coeffects))))}))

(reg-event-fx
 :upload-shot-video-server
 (fn [coeffects [_ blob]]
   (let [body (js/FormData.)]
     (.append body "video" {:name "movie.mov" :type "video/quicktime" :uri blob}  "video.mov")
     (.append body "key" "VAL")
     {:http-xhrio {:method :post
                   :uri (str server-uri "/api/upload-shot-video")
                   :body body
                   :on-success [:upload-success]
                   :on-failure [:upload-error]
                   :format (json-request-format)
                   :response-format (raw-response-format) #_(edn/edn-response-format)}}))
 
and the handler is the following:

zendevil 2021-01-25T06:11:26.266900Z

(defn upload-shot-video [req]
  (prn "uploading video")
  (prn "video is! " (-> req :multipart-params))
  (prn "video is " (-> req :params))
  (prn "video before is " (slurp (-> req :body)))
  (.reset (-> req :body))
  (prn "req full" (-> req))
  (prn "video after is " (-> req :body))
  (prn "video is! " (-> req :multipart-params))
  (<http://clojure.java.io/copy|clojure.java.io/copy> (-&gt; req :body) (<http://clojure.java.io/file|clojure.java.io/file> "./resources/public/video.mov"))

zendevil 2021-01-25T06:11:27.267Z

However, the request is not a multipart request, so I donā€™t see any entries in :multipart-params key

zendevil 2021-01-25T06:11:56.267600Z

and the entries I do see in the params key doesnā€™t have a :tempfile key so I canā€™t access the file thatā€™s uploaded

zendevil 2021-01-25T06:12:30.268100Z

Instead I see

{"_parts":[["video",{"_data":{"size":2971246,"blobId":"D002459C-47C5-4403-ABC6-A2DE6A46230A","offset":0,"type":"video/quicktime","name":"DCDE604A-954F-4B49-A1F9-1BCC2C2F37BC.mov","__collector":null}}],["key","VAL"]]}
when doing (-> req :params)

zendevil 2021-01-25T06:13:00.268600Z

thereā€™s a filename in the request map. Where is this file stored?

zendevil 2021-01-25T06:22:42.269Z

Iā€™m unable to extract the data from the request since thereā€™s no tempfile key

zendevil 2021-01-25T06:22:57.269400Z

and the request is not multipart

2021-01-25T06:34:05.270300Z

You can't pass a clojure map like that to a function that expects a js object

2021-01-25T06:38:15.271900Z

I am no js dev but these docs for append on form data https://developer.mozilla.org/en-US/docs/Web/API/FormData/append don't seem to match your 3 arg call to that method

2021-01-25T06:44:17.274Z

https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects has a js example, using form data to do a multiparty file upload, and you can see they construct a blob object, with the mimetype part of that, and pass it directly to append

zendevil 2021-01-25T07:49:49.275400Z

@hiredman, after changing to this:

(reg-event-fx
 :upload-shot-video-server
 (fn [coeffects [_ blob]]
   (let [body (js/FormData.)]
     (prn "blob is " blob)
     (.append body "video" blob)
     (.append body "key" "VAL")
     {:http-xhrio {:method :post
                   :uri (str server-uri "/api/upload-shot-video")
                   :body body
                   :on-success [:upload-success]
                   :on-failure [:upload-error]
                   :format (json-request-format)
                   :response-format (raw-response-format) #_(edn/edn-response-format)}}))
)
I find that multipart params does have the video and key keys, but the value to the video key is ā€œā€ and not the blob thatā€™s expected

2021-01-25T07:53:11.279100Z

You should ask in #clojurescript, but I would start by reading some docs. I don't know what library you are using that is actually making the request, but docs on that, I also think setting the format to (json-request-format) seems suspicious

2021-01-25T07:53:22.279400Z

So check the docs on that

zendevil 2021-01-25T07:56:08.279600Z

@hiredman Iā€™m using the cljs-ajax library

2021-01-25T07:56:34.280300Z

The readme for https://github.com/JulianBirch/cljs-ajax says it doesn't support file uploads

2021-01-25T07:57:19.281Z

Oh, I misread it

zendevil 2021-01-25T07:57:25.281200Z

@hiredman it says it doesnā€™t have ā€œdoesnā€™t have any other support for file uploadsā€

2021-01-25T07:59:56.281700Z

https://github.com/JulianBirch/cljs-ajax/issues/122 has some examples

zendevil 2021-01-25T08:01:42.282200Z

@hiredman Iā€™m using :body and not :params as mentioned

zendevil 2021-01-25T08:15:13.282700Z

wrapping the map with (clj->js) worked.

zendevil 2021-01-25T08:15:41.283300Z

@hiredman Thanks a lot. You are a savior. Had been stuck on this for two whole days.

svt 2021-01-25T10:58:15.283800Z

How do we test future in clojure?

svt 2021-01-25T10:58:46.283900Z

I want to test if a function which is passed to to future has been called or not

borkdude 2021-01-25T11:00:14.284200Z

(def state (atom nil))
(defn foo [] (reset! state true))
(def fut (future (foo)))
@fut
(is @state)

borkdude 2021-01-25T11:00:52.284400Z

or if you want to test the return value of foo, just return it from the future

borkdude 2021-01-25T11:01:04.284600Z

and then test the return value of @fut

svt 2021-01-25T11:02:12.284800Z

Thank you for the quick reply šŸ™‚

svt 2021-01-25T11:05:43.285Z

@borkdude Is there a way to do it without atom? Or just checking if the future is initiated?

borkdude 2021-01-25T11:07:15.285200Z

it depends on your usage. your question was: test if a function has been called, so I assume you want to test some side effect. the side effect depends on your specific program

svt 2021-01-25T11:12:08.285400Z

Yes, right

mpenet 2021-01-25T12:07:09.285700Z

you can do the same with promise as well

mpenet 2021-01-25T12:07:25.285900Z

but it's the same really

mpenet 2021-01-25T12:07:43.286100Z

you can also use a lib like spy

mpenet 2021-01-25T12:07:57.286300Z

https://github.com/alexanderjamesking/spy

mpenet 2021-01-25T12:08:16.286700Z

I quite like just relying on promise or atom (if you need to mutate more than once) personally

Christian 2021-01-25T14:34:37.291900Z

Just checking, if I get the current eco system correctly. I'm a beginner, so bear with me, if what I type sounds strange to you. ā€¢ Clojure is a hosted language on the JVM, which makes it easy to use Java libs with interop. ā€¢ There are implementations with JS and .Net, does that mean I can run .Net and JS code like the Java Interop? ā€¢ There is clj-python, I understand, that it runs python code on the side and reimports the results back into the clojure environment. So I can call (slower than normal?) Python libs like Java interop? ā€¢ With GraalVM that is hosting Python on the same VM as my Clojure, does that just speed up my Python calls because they don't need to be external? What is the benefit to the clj-python lib?

2021-01-25T14:43:01.292Z

clj-python can utilize an entire python ecosystem. GraalVM provides a Python 3.8 compliant runtime.

2021-01-25T14:43:29.292200Z

yes on the first bullet. On the second bullet, ClojureScript is compiled to JavaScript, and has ways to call JavaScript libraries from ClojureScript code, which syntactically are often similar to calling Java methods from Clojure. I haven't used ClojureCLR, but I believe it can make calls to .Net libraries via interop similarly to Clojure->Java calls.

Andy Wootton 2021-01-25T15:06:47.293100Z

I wouldn't risk anything important on the continued future of the .Net version right now. It seems to have lost momentum, even at Microsoft :-)

šŸ‘€ 2
danielneal 2021-01-25T16:18:23.294Z

I know of one library for generating clojure documentation from tests - http://helpshift.github.io/hydrox/ are there any others? Preferably one that doesn't depend on leiningen

Mno 2021-01-25T16:31:36.294300Z

(thereā€™s also #babashka which is clojure as a scripting language (basically), and it has a variety of interop options)

borkdude 2021-01-25T19:16:20.295100Z

would this be "too clever", shortcutting cond's expansion?

(defmacro cond*
  [&amp; clauses]
  (when clauses
    (let [fst (first clauses)]
      (if (keyword? fst) (second clauses)
          (list 'if fst
                (if (next clauses)
                  (second clauses)
                  (throw (IllegalArgumentException.
                          "cond requires an even number of forms")))
                (cons `cond* (next (next clauses))))))))


(require '[clojure.walk :as walk])

(walk/macroexpand-all '(cond (odd? x) x :else 1))
;;=&gt; (if (odd? x) x (if :else 1 nil))

(walk/macroexpand-all '(cond* (odd? x) x :else 1))
;;=&gt; (if (odd? x) x 1)

phronmophobic 2021-01-25T19:23:33.296Z

seems reasonable, but at least for the jvm and js, wouldn't the JIT also do this kind of analysis?

borkdude 2021-01-25T19:25:05.296700Z

probably, but why rely on that if you can cut it out earlier

borkdude 2021-01-25T19:25:28.297400Z

I would have expected Compiler.java to have done a similar trick, but can't find it

phronmophobic 2021-01-25T19:25:33.297500Z

trying to think of a case where evaluating a keyword may have some sort of side effect that a program might intentionally or unintentionally depend on, but I can't think of any

šŸ‘ 1
alexmiller 2021-01-25T19:26:11.298200Z

any compile-time constant object could be treated as an else there

caumond 2021-01-25T19:26:19.298400Z

Interested in the answer

borkdude 2021-01-25T19:26:40.299100Z

sure, keyword? could be (constant? ...)

alexmiller 2021-01-25T19:26:47.299200Z

keyword certainly is common, but could also be string, true , 42 , etc

alexmiller 2021-01-25T19:27:35.299800Z

I mean the presumption here is that it's faster but probably doesn't matter with jit

phronmophobic 2021-01-25T19:27:35.299900Z

it's sort of contrived, but a deepwalking macro or code analysis tool that uses clojure.walk/macroexpand looking for keywords would be affected by this.

borkdude 2021-01-25T19:28:05.300600Z

it would be faster in an interpreter, less code to process during evaluation. just wondering of what could go wrong

phronmophobic 2021-01-25T19:28:41.300900Z

so if you had a macro that would automatically produce defs for all keywords in a macro-expanded expression, it would then fail to find any keyword that is a test in a cond expression

borkdude 2021-01-25T19:29:03.301500Z

maybe if a macro that was called later on had a side effect

borkdude 2021-01-25T19:29:30.302100Z

but maybe one should not rely on that

phronmophobic 2021-01-25T19:29:40.302300Z

as well as any code analysis tool looking for keywords that macro expanded first

phronmophobic 2021-01-25T19:29:51.302600Z

so it may introduce some edge cases

dpsutton 2021-01-25T19:30:22.303500Z

small paper testing this hypothesis: > Small, deterministic programs reach a steady state of peak performance. https://arxiv.org/abs/1602.00602

borkdude 2021-01-25T19:30:30.303800Z

like:

(cond (odd? x) x :else 1 :foobar (macro-that-launches-rocket))

2021-01-25T19:30:36.304Z

Rather than changing cond , is it feasible for the interpreter you have in mind to notice (if &lt;constant-expression&gt; ...) forms and optimize those 'in place'?

borkdude 2021-01-25T19:31:00.304200Z

yeah, that's also possible

2021-01-25T19:31:09.304400Z

That would cover this case, and potentially others.

borkdude 2021-01-25T19:31:18.304600Z

right

dpsutton 2021-01-25T19:31:46.304800Z

dpsutton 2021-01-25T19:38:03.305200Z

cool paper about the reliability of the JIT and how long (if ever) it might take to warm up

ghadi 2021-01-25T20:26:26.306800Z

@borkdude @alexmiller a ticket+patch exists somewhere for ifā€™s with constant tests

bronsa 2021-01-25T20:34:31.307600Z

āž• why would you want to do this on a specific macro instead of a simple general optimisation for if in the compiler

borkdude 2021-01-25T20:41:31.308600Z

@bronsa yes, I also wondered about the compiler, I didn't see specific code for that

borkdude 2021-01-25T20:41:54.308800Z

thanks

2021-01-25T21:06:23.308900Z

the clojurescript compiler does a lot of similar kind of, I dunno what to call them, peephole optimizations? I can't think of how this would break anything, but I am wary of that kind of thing in general because as a result cljs has more corner cases with core.async, and sometimes you can get weird double code evaluations

2021-01-25T21:06:41.309100Z

(I guess it isn't the compiler that does it, it is the macros themselves)

2021-01-25T21:17:49.309300Z

https://clojure.atlassian.net/browse/ASYNC-91 and https://clojure.atlassian.net/browse/ASYNC-128 are examples of cljs+core.async issues due to macros trying to be too clever

borkdude 2021-01-25T21:34:09.309500Z

wow, interesting

borkdude 2021-01-25T21:35:22.309700Z

> also or and and in clojurescript both run analysis as part of the macro expansion, which can lead to macros be expanded multiple times yeah, I can see that leading to unexpected behavior...

2021-01-25T21:51:12.309900Z

there is another issue somewhere, the cljs compiler I think tries(tried?) to optimize literal list construction in a way that caused multiple evaluations as well

borkdude 2021-01-25T21:54:12.310100Z

I've also seen some similar things with side effects in CLJS spec macros