Fork me on GitHub
#beginners
<
2021-09-12
>
stagmoose08:09:23

When I was using hiccup, I found I cannot use it inside anonymous function. when this works:

(defn display-todo [] 
  (let [todos (re-frame/subscribe [::subs/mock-data])]
    (log (clj->js @todos))
    (map (fn [x] [:span (str (:block/string x))]) @todos)))
but this did not work:
(defn display-todo [] 
  (let [todos (re-frame/subscribe [::subs/mock-data])]
    (log (clj->js @todos))
    (map #([:span (str (:block/string %))]) @todos)))
How can I explain this?

hiredman08:09:17

Those are both anonymous functions

hiredman08:09:28

But they are not equivalent

hiredman08:09:46

#(x) is the equivalent of (fn [] (x))

hiredman08:09:50

So #([...]) is (fn [] ([...]))

hiredman08:09:20

Not (fn [] [...])

hiredman08:09:25

Which is why the stacktrace you got said something about calling a function with the wrong number of arguments, because you are invoking a vector as a function (which you can do) with the wrong number of arguments

👌 2
stagmoose08:09:29

gocha, thanks for the help!

cdpjenkins09:09:28

</lurk> Just curious, is it actually possible to write it such that it is equivalent to (fn [] [...])? Or is it always necessary to use the longhand notation in that case?

Ed10:09:19

You could write #(vector 1 2 3) or #(do [ 1 2 3])

👍 2
Ed10:09:11

But it's generally better to use (fn ...)

👍 2
quoll18:09:23

This made me curious, so I looked it up. Yes, using (fn [] [1 2 3]) is better than either of: • (fn [] (vector 1 2 3))#(vector 1 2 3) However, there’s no practical difference to: • #(do [1 2 3]) It’s purely about what you might think is the better idiom. Personally, I think that do implies a sequence of operations to perform, typically with a side-effect (such as printing or logging), so I wouldn’t use it here.

👍 6
quoll18:09:16

The reason why it’s “better” is that calling vector loads 2 strings, with contents: “clojure.core” and “vector”, then calls the function clojure.lang.RT.var to get the vector function. Then it loads the remaining args over it in the stack and calls it. Using the literal syntax […] is more direct. It makes a direct call to either clojure.lang.Tuple/create (for vectors of 6 or fewer) or clojure.lang.RT/vector (which is what the Tuple/create method calls)

👍 6
Benjamin09:09:10

;; how do I achieve this
(defn do-stuff []
  
  ;; every 30ms call this
  ;; if it throws an exception, cancel all this
  (ProgressManager/checkCanceled)

  ;; do something presumably expensive
  (go ...? )

  )

Frosku09:09:44

If 'all this' has side effects, you'll need some way of signalling that you want to undo those side effects (or ideally capture them all and only process them at the end)

Benjamin10:09:15

Yea that is fine. Basically what I would like to have is maybe a channel like timeout but for this checkCanceled thing. But checkCanceled needs to be on the same thread. I don't have experience yet with sticking together those channels

Fredrik13:09:13

One common idiom is to use a channel (called a "shut-down channel" or "control channel") to signal the go-loop to stop. This requires the go-loop to periodically check for values put on this channel.

Fredrik17:09:27

Let me know if you want some code examples

Benjamin06:09:20

Yea what i was stuck with was that this checkCanceled function only threw an exception on the initial thread so naively putting it into go didn't work. For now iade it work with future

Frosku09:09:46

I'm not sure if I'm just missing it but none of the example sente apps seem to touch on websocket subscriptions. I'd like ws clients to be able to send i.e. {:subscribe {:article 14}} and get all future updates for that key until they unsubscribe. What's the best way to go about doing that?

popeye15:09:28

I was going the future function and can we use this for all the function call which results are independent to each other?

Chase16:09:47

I was running through a little intro tutorial as a morning warmup but encountered a snag in solving one of the exercises

Chase16:09:16

;; Write a zero-argument function that returns the `identity` function                                                  
                                                                                                                        
(defn gen-identity                                                                                                      
  [] ; zero arguments                                                                                                   
  identity)                                                                                                             
                                                                                                                        
;; EXERCISE                                                                                                             
;; Fix this function so that it returns a function that _behaves_                                                       
;; like the identity function (don't return `same`, or `identity`).                                                     
                                                                                                                        
(defn gen-identity-v2                                                                                                   
  []                                                                                                                    
  (fn [x] x))                                                                                                           
                                                                                                                        
;; EXERCISE                                                                                                             
;; Replace 'FIX1 with a call to the `gen-identity` function,                                                            
;; and 'FIX2 with a call to the `gen-identity-v2` function,                                                             
;; such that the following evaluates to true.                                                                           
                                                                                                                        
(= identity                                                                                                             
   (gen-identity)                                                                                                       
   (gen-identity-v2)) ;; false

Chase16:09:05

My gen-identity-v2 functions makes it false. What would you use there to make the last expression evaluate to true?

NoahTheDuke16:09:03

I would expect that to return false so I'm confused

Chase16:09:01

The exercise wants it to return true so I was asking how to change the gen-identity-v2 function to make that happen

Chase16:09:28

I just added that comment to show what I was getting, not what the exercise called for. My bad

NoahTheDuke16:09:49

Right, my apologies, I expect that the equality the exercise is performing to return false, because (as I remember) anonymous functions aren’t equal to other functions , so I’m also not sure how to write the correct answer

Chase16:09:46

Right? Haha. I figured it was to teach the concept of function equality but then it never supplies an answer or explanation.

Chase16:09:05

Oh wait, I did find the solutions branch. They had the same gen-identity-v2 function as I did but they called it with the identity function:

(= identity
   (gen-identity)
   ((gen-identity-v2) identity))

Chase16:09:30

Fair enough I guess

NoahTheDuke16:09:36

Hm yeah, that works but is not what I expected from the text. Glad you have a solution!

Chase16:09:14

In context they were talking about higher order functions and were doing similar things to that so that's on me a bit.

👍 2
Chase19:09:56

Is there a performance difference between these 3 approaches to reach into a nested map? Any other pros or cons or would you consider it entirely subjective?

(def foo {:a 1 :b {:c 2 :d 3}})
                                                                                         
(get-in foo [:b :d]) ;; 3                                                                                               
((comp :d :b) foo) ;; 3                                                                                                 
(-> foo :b :d) ;; 3                                                                                                     

Chase19:09:35

Side question: How would you approach a quick benchmarking of such a thing?

seancorfield19:09:37

There's a :bench alias in my dot-clojure, in case you're using that.

Chase19:09:26

Yep, I am, I'll take a look

seancorfield19:09:33

As for pros/cons, I tend to use the third approach most of the time but I think for most people the first approach is more "obvious" and readable. The second is way too cryptic.

seancorfield19:09:58

(! 647)-> clj -A:bench
user=> (require '[criterium.core :refer [bench]])
nil
user=> (def foo {:a 1 :b {:c 2 :d 3}})
#'user/foo
user=> (bench (get-in foo [:b :d]))
Evaluation count : 1155680820 in 60 samples of 19261347 calls.
             Execution time mean : 44.709843 ns
...
user=> (bench ((comp :d :b) foo))
Evaluation count : 2855798280 in 60 samples of 47596638 calls.
             Execution time mean : 13.269613 ns
...
user=> (bench (-> foo :b :d))
Evaluation count : 2365929660 in 60 samples of 39432161 calls.
             Execution time mean : 17.841840 ns
...
user=> 

seancorfield19:09:51

I'm a bit surprised the second approach is faster than the third.

2
Fredrik19:09:09

I'd be interested in knowing why that's the case. The fastest seems to be (.valAt (.valAt foo :b) :d), but now we're getting a little silly

NoahTheDuke19:09:55

get-in is the slowest one? That’s wild. It feels the most idiomatic to me! I only use threading macros when adding in other functions like first

Fredrik19:09:31

get-in is slow because it loops acroos the keys, each time checking for a missing key. EDIT: Actually it is because it uses reduce1, a slower version of reduce.

seancorfield19:09:27

We're talking a "few" nanoseconds here -- this is always the issue with being concerned about low-level performance: it's not worth looking at unless you have a demonstrable performance problem and you've profiled your code and identified a bottleneck.

👍 4
seancorfield19:09:34

Also, performance of low-level stuff can change from (Clojure) version to version so optimizing for it "in advance" is pretty never worthwhile.

quoll19:09:59

I would guess that the native compilation of (comp :d :b) and executing that on the object may be more efficient than when it compiles calling :b then :d in order. But I haven’t ever really looked up how to access the native instructions that get generated, and without access to that I can only speculate

quoll19:09:16

note: if someone knows how to dump compiled code (and I know it can be done), then I’d love to hear how. This could save me hours of Googling 🙂

Fredrik19:09:06

Or do you want the assembly instructions?

quoll19:09:18

I run javap regularly, so I’m good there. But I don’t know how to get the native instructions from the JIT once the compilation threshold has been met

quoll20:09:31

Thank you!

😀 2
Ben Sless13:09:25

@U051N6TTC you can use JITWatch as well, I used it a few times

Ben Sless13:09:40

@U024X3V2YN4 if you look at the implementations you'll find the fastest path without dispatching directly on .valAt is invoking the map directly on the key, which calls valAt with no dynamic dispatch

Fredrik14:09:57

You mean calling (.invoke foo :b) instead of (.valAt foo :b)?

Ben Sless14:09:15

Since foo is a map you can just (foo :a)

Fredrik14:09:05

(:a foo) is consistently slightly faster on my benchmarks. EDIT: no, it's not. I was looking at the wrong table

Ben Sless14:09:09

If you want to compose it generically you should nil check or check it's ifn?

Ben Sless14:09:38

hang on I have the numbers

Ben Sless14:09:30

damn, I don't have the numbers checking the difference between calling the keyword and calling the map. Need to run those with JMH, too

Ben Sless14:09:19

you can look at the implementation, though

Fredrik14:09:55

Yes, I agree, calling the map saves some type checking

Ben Sless14:09:43

The remaining question is how it gets JIT compiled and if it's a megamorphic call site, but I couldn't figure it out just by looking at it

Ben Sless14:09:10

Would also depend on your JVM version, probably

Ben Sless14:09:30

regardless, the biggest performance impact here is the interation by way of reduce1 in get-in

Ben Sless14:09:52

even if you reimplement get-in as (reduce get m ks) you'll see a speedup

Fredrik14:09:16

Yes, as I mentioned above get-in checks for missing values at each key.

Ben Sless14:09:39

That's just for the not-found arity

Fredrik14:09:40

Oh right, so what's the purpose of reduce1 ?

Ben Sless14:09:16

It's defined before reduce because reduce can only be defined after you load protocols

Ben Sless14:09:27

It's a correct but slower version

Fredrik14:09:17

Got it, thanks 🙂

Lukas20:09:56

Hey, for a while now, Emacs opens the Cider-result buffer at the bottom like this | | | | | _______________ | | C-c C-p result | I would really like to get my old default back: open the buffer on the right side | | C-c C-p | | | results | | | here | But I can't figure out how to do it. I'm not even find the right keywords to search for this problem. Any help is appreciated 😄

Ed20:09:35

Emacs has a few functions and settings that relate to window placement. This might help : https://stackoverflow.com/questions/7997590/how-to-change-the-default-split-screen-direction ... If you resize the window to short and wide or real and thin, does it change the behaviour? If so, you can just change the preferred split size.

👍 2
sova-soars-the-sora22:09:19

I think C-x 3 is vertical and C-x 2 is horizontal

practicalli-john22:09:27

Placement can also be influenced by how much space Emacs has to create a new window as a new column

Fredrik23:09:09

Are you using popwin? If not, it could be worth checking out. After install, eval (push '("*cider-result*" :noselect t :position right :width 90) popwin:special-display-config) and see if it makes any difference.

Lukas08:09:26

I tried all suggestions so far but couldn't make it work 🙁

Fredrik20:09:31

There is an #emacs channel available, if you don't get help here.

👍 2
😮 2