Fork me on GitHub
#clojurescript
<
2022-10-20
>
genRaiy09:10:31

I wrote an equality checker for floating point numbers to 2 places cos JS kept putting 00000009 at the end of my floats ... it's a hack and I would welcome better ideas

genRaiy09:10:40

(defn equal-fixed-length?
  "JS does not do reliable floating point checks, so we implement equality for two or more numbers to fixed decimal places"
  ([n0 n1] (= (.toFixed n0 2) (.toFixed n1 2)))
  ([n0 n1 & more]
   (if (= (.toFixed n0 2) (.toFixed n1 2))
     (if (next more)
       (recur n1 (first more) (next more))
       (= (.toFixed n1 2) (.toFixed (first more) 2)))
     false)))

genRaiy09:10:35

[ I obviously just copied the CLJ = code ]

Hankstenberg11:10:29

Has anybody managed to get react-form-hook to work with Clojurescript already? I'm fighting with its odd use of javascript/react features:

<input defaultValue="test" {...register("example")} />
the really weird thing is that "register" is a function, not an array or object.. The alternative syntax is different, but also odd:
<Controller
        name="checkbox"
        control={control}
        rules={{ required: true }}
        render={({ field }) => <Checkbox {...field} />}
what would be the cljs/reagent equivalent for the render function?

βœ… 1
p-himik11:10:42

> "register" is a function, not an array or object But it's called in that example. And it returns an object that is being destructured, or whatever ... is called in JS.

βœ… 1
πŸ™Œ 1
p-himik11:10:19

A Reagent version of the second code block is:

[:> Controller {:name "checkbox"
                :control control
                :rules #js {:required true}
                :render (fn [^js obj]
                          (r/as-element [:> Checkbox (some-shallow-js->clj-impl (.-field obj))]))}]
That some-shallow-js->clj-impl might not be needed - not sure, would have to check. That r/as-element is also not needed if you call reagent/createElement directly instead. BTW the question would be better suited for #C0620C0C8.

βœ… 1
πŸ™Œ 1
Hankstenberg11:10:21

Wow, thanks, got it! πŸ™‚

jewiet11:10:07

I am using recharts library https://recharts.org/en-US to draw a line chart. For the data below I want to show a 7 days data with the :date on the x-axis and the :y values on the y-axis. For the missing dates "2022-09-11", "2022-09-12" I would like to drop the line to zero.

(def mock-data
[{:date "2022-09-13" :x "2022-09" :y 3.2675}
  {:date "2022-09-14" :x "2022-09" :y 2.01134615}
  {:date "2022-09-15" :x "2022-09" :y 1.6284127}
  {:date "2022-09-16" :x "2022-09" :y 1.8054386}
  {:date "2022-09-17" :x "2022-09" :y 1.67258065}])
I think I need to add the zero value into the data for each date that has no data. I saw an example using interval range for a different library but I can't seem to find an example on what i am trying to do with recharts library. Anyone has encountered same issue and how did you solve it?

Paul12:10:37

I've done this in js by stubbing in an object(s) with a 0 in the appropriate field:

{:date "2022-09-11" :x "2022-09" :y 0}

alex14:10:35

Can anyone help me with developing a mental model for promesa/bind vs. promesa/then? What is the difference between them?

p-himik14:10:47

bind is not documented, so I would avoid using it. With CLJS, the default implementations are the same. With CLJ, there's some difference but it's not clear to me. bind will call the passed function as-is whereas then will wrap convert its result into a promise. Whether there's a real difference, I don't know - probably depends on how you'll use the result of a coll to one of those functions. With CLJ,

πŸ™Œ 1
p-himik14:10:19

And initially bind was introduced with "A convenient alias for then." docstring (no clue how it was convenient and why not just write (def bind then)), but later on the implementation was changed and the docstring was removed, with this in the changelog:

- Make the `bind` function behave as it should behave (like bind and
  not being `then` alias). **This is technically a breaking change**,
  the `bind` function should have been implemented in terms of `bind`
  operation and not be an alias for `then`.
Couldn't be less clear, to be honest...

πŸ˜„ 1
alex15:10:22

Thanks -- that's all really helpful! I'm in cljs-land, and I'll just stick to using then. Just wanted to make sure I wasn't missing something πŸ™‚

πŸ‘ 1
skylize20:10:17

The difference is described in the IPromise protocol.

(defprotocol IPromise
...
(-bind [_ f] [_ f executor]
    "Apply function to a computation and flatten.")
...
(-then [_ f] [_ f executor]
    "Apply function to a computation and flatten if promise found.")
...
But this does not actually work in CLJS, because of being implemented as a JS Promise, which will only supports the then variant. In JS you can start with an initial Promise, and chain then infinitely without ever explicitly returning another Promise. Or you can nest Promises infinitely. And in all cases these will automatically flatten down to exactly 1 Promise wrapping exactly 1 value. So this is what you get for both then and bind in CLJS. With something like an IO Monad (from which Promises are loosely modeled), you must manage nesting yourself. A function provided to bind should return a new IO and if you get multiple IOs wrapped around each other, you need to unwrap them or traverse inward to find the contained value. As such, bind on the JVM tries to align better with monad laws, requiring that you explicitly return a new Promise.
(-> (p/resolved :foo)
    (p/then identity)
    (p/then prn))
;; :foo
;; ...CompletableFuture...

; With bind you get a wrapped exception
(-> (p/resolved :foo)
    (p/bind identity)
    (p/then prn))
;; ... CompletableFuture ... CompletionException ...

; Need to use Promise-returning equivalent to identity instead
(-> (p/resolved :foo)
    (p/bind p/resolved)
    (p/then prn))
;; :foo
;; ...CompletableFuture...
As currently implemented, I'm not to sure how you would normally get a value out at the end, without falling back on a then-based function, which might be part of why it is not documented. There is p/extract, but I would expect that to block (nice for introspection, but otherwise defeats the purpose of promises).

p-himik20:10:27

Ah, I was wondering where the name bind came from. Seems like it's from monads then. :) Thanks!

vlad_poh15:10:52

How do i write the following line in clojurescript?

const markerCluster = new markerClusterer.MarkerClusterer({ map, markers });
Tried the following (js/google.maps.markerClusterer.MarkerClusterer. [map markers])

p-himik15:10:10

1. Require markerClusterer properly, assuming that's possible at all 2. Use (new (.-MarkerClusterer markerClusterer) #js {:map map, :markers markers}) It's possible that MarkerClusterer could be required on its own, then you would be able to use (MarkerClusterer. #js {...}).

πŸ™Œ 1
vlad_poh16:10:14

@U2FRKM4TW it works but i don’t understand how {map, markers} becomes {:map map :markers markers}

Sam Ritchie17:10:43

I think that is JS syntactic sugar for the version that @U2FRKM4TW expanded

πŸ‘ 1
localshred17:10:51

in es6, {map, markers} is short-hand js for {map: map, markers: markers}, where you only need to specify the named vars if the map key should be the same name

πŸ‘ 2
Sam Ritchie17:10:53

The js version works too if you write the explicit object argument

Steph Crown17:10:54

Hi. I have an issue with my ClojureScript project which I created with shadow-cljs. Whenever I update my code and I check my browser, I see that both the old code and the updated one is executed. For example, I am listening to a click event on an HTML element and I log the text "Hello world" to the console when the element is clicked. I check my browser and this works out fine. Then I decide to change the text that is logged to "Hello Clojurians". If I save and go back to my browser, clicking the HTML element will first log "Hello world" before logging "Hello Clojurians" to the console. I guess one way or the other my previous code is being cached but I don't know how to prevent this.

thheller17:10:44

if you attach the even handler manually then you also need to remove it manually

Steph Crown17:10:28

Yeah, I attached the handler manually.

thheller17:10:56

usually people use something like reagent/react, which takes care of that

thheller17:10:11

manual DOM interop and hot-reload is a bit icky and manual

Steph Crown18:10:05

So I am thinking of removing the listeners before unload. Do you think it'll work?

thheller18:10:04

when you attach the event handler you are doing basically that, so if you add that indirection it might work too

thheller18:10:29

of course only works if you only attach the event handler once, not on every reload

Steph Crown18:10:36

Yeah thanks. I was able to remove the event listener before load and now it works well. Thanks @U05224H0W @U3BALC2HH.

Noah Bogart19:10:12

Is it possible to write (ns foo) (ns bar (:require [foo :as f])) in a clojurescript repl? I can't seem to do it

Noah Bogart19:10:07

Which is to say, I'm experimenting with how requires work in clojurescript, and I don't want to stand up a whole clojurescript project just to test this, but unlike the clojure repl, i can't get this "basic" functionality to work

$ clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.11.54"}}}'
Clojure 1.11.1
user=> (require '[cljs.main])
nil
user=> (cljs.main/-main)
ClojureScript 1.11.54
cljs.user=> (ns foo.f) (defrecord FooVar [])

foo.f/FooVar
foo.f=> (ns bar.b (:require [foo.f :as f]))
Execution error (ExceptionInfo) at cljs.repl/ns->input (repl.cljc:203).
foo.f does not exist
bar.b=>

p-himik20:10:06

> I'm experimenting with how requires work in clojurescript, and I don't want to stand up a whole clojurescript project just to test this I can't answer your immediate question, but I would advise against doing namespace- and var-related experiments in a REPL if you want to apply that knowledge in a regular compiled project. Lots of things work differently in a CLJS REPL. And starting a new project is quite easy - either manually or e.g. with deps-new.

πŸ‘ 1
Noah Bogart20:10:24

That's a bummer, thanks.