Fork me on GitHub
#clojure
<
2021-11-25
>
drewverlee01:11:47

I keep having to come back to read this https://clojure.org/guides/repl/enhancing_your_repl_workflow#writing-repl-friendly-programs to remember how to write repl friendly programs. Which means i'm missing something. e.g Why would redefining a var allow you to change the program in the other thread, but not changing the text directly? I'm assuming its because in the first case, we have a pointer to the var memory space in the other process.

lsenjov02:11:40

Which example are you talking about specifically?

lsenjov02:11:18

print-number-and-wait 3/4 ?

lsenjov02:11:20

In general, it's the difference between calling the function as a value, or as a reference (probably wrong terminology but w/e) When you use run!, the function in example 1 is already defined and will not change. In example 2, when you call print-number-and-wait it looks up the current value of print-number-and-wait and calls it with i Same with 3 and 4. In 3 it's given as a value, in 4 it specifically says look up the value of var print-number-and-wait when calling this

drewverlee02:11:30

would it be correc to say that in this clojure code

(defn print-number-and-wait
  [i]
  (println i "green bottles, standing on the wall. ♫")
  (Thread/sleep 1000))

(future
  (run!
    print-number-and-wait
    (range)))
print-number-and wait within the future, is a symbol when run! is called it will be passed the value of that symbol? That's why it can't be re-evaled, because the global scope has no pointer to that var. In this case
(future
  (run!
    #'print-number-and-wait ;; mind the #' - the expression evaluates to the #'print-number-and-wait Var, not its value.
    (range)))
run is passed (var print-number-and-wait) which evals to the symbol, which again means were passing the symbol is in global scope

👍 1
drewverlee03:11:49

Can anyone translate this to english clojure speak?

(defn foo [] "foo")

;; interesting bit...

#'core/foo;; => #'core/foo

(#'core/foo);; => "foo"
I would say when the clojure compiler via the CLojure Reader see's #'core/foo it says, oh yea, those characters #' mean " we hope the next characters before a whitespace match or book of existing symbols. Oh great it is because we saw (defn foo...) Now, when it reads this at run time, it's going to think ok #'core/foo just just return itself (as opposed to it's value , the inner part of our defn "foo" But here is where i can't describe the next part. something about the difference between #'core/foo and (#'core/foo) gets me. Maybe i'm not sure how to bridge the gap and verbalize what () means at run time?

1
lilactown03:11:42

#'core/foo is a reader literal for (var core/foo): https://clojuredocs.org/clojure.core/var

👍 1
lilactown03:11:10

var returns an object that, when dereferenced, will lookup core/foo in clojure's table of defined symbols and return it

drewverlee03:11:14

> return it It being the value of the key/sybmol?

drewverlee03:11:14

and ty for providing some insight into this!

lilactown05:11:05

that's right

👍 1
lsenjov03:11:11

Is there a difference between (core/foo) and (#'core/foo) ?

lilactown03:11:55

when invoked, it will lookup the value of core/foo and then invoke that value

lilactown03:11:14

not normally, but it can be useful for doing what is called late binding

lilactown03:11:26

in the case of:

(core/foo)

(#'core/foo)
there will likely be no runtime behavior difference

gratitude 3
Jakub Holý (HolyJak)09:11:32

Hi! Is there a nicer way to write just-once initialization like this

(swap! my-resource
         #(if %
            %
            (expensive-create-resource)))
I feel there must be but cannot figure it out... 🙏

p-himik09:11:01

Well, the if % part can be replaced with just or. But if you never change my-resource again, then use delay.

Jakub Holý (HolyJak)09:11:11

Excellent ideas, thank you!

Jakub Holý (HolyJak)09:11:14

I cannot use delay here though because this fragment is from inside a function and it depends on an argument (which it passes to expensive-create-resource ). But or is certainly nicer!

p-himik09:11:47

Ah, then maybe promise.

p-himik09:11:15

(but depending on something doesn't make delays unusable, as long as things are immutable)

Jakub Holý (HolyJak)10:11:54

I meant I cannot (def resource (delay ...)) b/c I do not have the runtime info I need there.

pithyless11:11:01

@U0522TWDA there is a race condition here; as long as expensive-create-resource is expensive, but still idempotent.. I guess that's ok ;)

1
pithyless11:11:52

I think you would actually need to do:

(def my-resource (atom nil))

(swap! my-resource #(or % (delay (expensive-create-resource ,,,))))

; and then maybe a helper function to hide the double-deref detail from others:

(defn resource [] @@my-resource))

p-himik11:11:36

If that function is idempotent and free of side effects, then there is no race condition.

Jakub Holý (HolyJak)12:11:40

It is sideeffectful and not idempotent. The race cond. is because the fn passed to swap can possibly be run multiple times, right?

Jakub Holý (HolyJak)12:11:24

I have realised I can actually refactor it to use (def ... (delay ..)) 😌

👍 2
mateus.henrique.brum13:11:27

Hello everyone, I am a noob in clojure so I have decide to make a small tic-tac-toe game to start learning it. I came across some basic but important concepts when try to decouple my UI form the Core. Two big pertinent questions are: Imagine I have to receive a stream of events (read-line) then compute the result and decide if I should proceed or getting the next item to compute again, what is the best way to implemente it? I implement something with recursion, my first try was with reduce but it apparently makes no sense as it is a infinite collection (not sure here as my understand to lay sec is poor)

(defn start-tic-toc-toe [board player] 
  (println (board-ui board))
  (println (turn-of player))
  (let [winner? (winner-in-board? board)
        cords (read-cord)]
    (if (> winner? 0)
      {:winner winner?
       :board board}
      (recur (play-in-board board player cords) (if (= 1 player) 2 1)))))
There are loads of down sides here, one is the testability, as I need to embed the (read-cord) -> (read-line) inside. Another is the fact I have some continuation logic (if (> winner? 0) that is not really an UI responsability. Any advice to improve it? Thanks. PS: https://github.com/mateushenriquebrum/tic-tac-toe/blob/development/src/tic_tac_toe/ui.clj

jkrasnay18:11:02

I would suggest having a look at this video: https://youtu.be/vK1DazRK_a0

jkrasnay18:11:14

You want most of your functionality to be in pure functions. The call to read-line is not pure, so you want to isolate just that part off into your top-level loop and delegate the rest to pure functions called from the loop.

jkrasnay18:11:24

Raf does a much better job at explaining this in the video.

mateus.henrique.brum12:11:05

Cool, thank you so much :)

Diego15:11:49

Hello, I am trying to make a simple macro for wrapping some functions into an more succinct syntax (for creative live coding purposes). My problem is that with cider-macro-expand-1 I get what I want, but it fails when I actually try to compile the code.

(defmacro on-event
  "Simply passes the `:keys` to the function"
  [f-body]
  `(fn [{{:keys [index dur dur-ms] :as data} :data}]
     (let [at-index #(wrap-at index %)] ~f-body)))
I get the following error:
1. Caused by clojure.lang.ExceptionInfo
   Call to clojure.core/fn did not conform to spec.
   #:clojure.spec.alpha{:problems
                        ({:path [:fn-tail :arity-1 :params],
                          :reason "Extra input"
...
Any help and also resources on writing macros in general would be greatly appreciated.

p-himik15:11:05

When creating anaphoric macro, you have to use quoting to make sure that symbols inside the syntax quote do not become qualified. So in your case, that index will become my.ns/index, same with dur and all the rest. To fix it, prepend ~' in front of all the forms that contain symbols that must not be qualified.

p-himik15:11:16

So, this should work, assuming wrap-at exists in the namespace that defined the macro:

(defmacro on-event [f-body]
  `(fn [~'{{:keys [index dur dur-ms] :as data} :data}]
     (let [~'at-index #(wrap-at ~'index %)] ~f-body)))

p-himik15:11:19

Regarding cider-macro-expand-1 - does its output differ from macroexpand-1? If so, perhaps it's a bug.

Diego15:11:19

Great, thanks @U2FRKM4TW.

👍 1
true.neutral16:11:59

Hello dear clojurians! I'm thinking of writing a financial visualization/analysis application that'd, among other things, allow me to visualize stock price candlestick charts + a number of indicators as lines (for example, stock price 50 days moving average), and to draw simple figures like straight lines on top of them interactively. I tried to search for a quick answer on this, but failed - graphing is a new subject to me, and the information looks a bit overwhelming, I can't get a definitive answer from my search. Hence I'd like to ask 2 questions: 1. Should I stick with just Clojure on top of JVM, or should I look into Clojure as backend and ClojureScript as a frontend? (the latter would be more difficult to me as I'm no frontend dev and don't actually know how JS/browser works; but I suspect the interactivity bit should work better with JS as I suspect JS has more graphing libs than Java -- maybe I'm wrong); 2. Could you point me in the right direction of some library that can make this possible with not a whole lot of effort? (so when I'm spending time diving in to it, I know I don't spend it for nothing just to hit a brick wall later and realizing I have to re-write half of the library to make it do I what I want it to) Thanks in forward.

dharrigan17:11:18

There's a guy here who did a series of videos on twitch, about doing the same thing(ish). All in clojure/script. Perhaps you may get some pointers?

dharrigan17:11:13

(so, all entirely possible to do in Clojure/script) 🙂

vlaaad18:11:34

Can be used from clj iirc

respatialized18:11:51

You should also look into https://github.com/jsa-aerial/hanami for plotting purposes. The vega-lite ecosystem lets you specify plots as data (either JSON or EDN) which can be done either on the JVM or in JS. https://vega.github.io/vega-lite/examples/layer_candlestick.html

👀 1
true.neutral20:11:24

Thank you very much! I'll look into all these 🙂

true.neutral13:12:17

vega-lite turned out to be a godsend. While not as interactive as I'd hope to, it's very simple to slap stuff together fast, and it's gorgeously looking and customizable as hell. As for the Clojure side, for now I chose oz, very easy and clean to use.

👍 1
true.neutral13:12:52

Thank you for your help again!