This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-01-12
Channels
- # announcements (2)
- # babashka (26)
- # beginners (48)
- # calva (32)
- # cider (23)
- # clj-kondo (61)
- # cljfx (3)
- # clojure (93)
- # clojure-australia (2)
- # clojure-europe (23)
- # clojure-losangeles (1)
- # clojure-nl (5)
- # clojure-uk (4)
- # clojurescript (46)
- # cloverage (9)
- # code-reviews (1)
- # copenhagen-clojurians (1)
- # cursive (39)
- # data-science (6)
- # datahike (8)
- # deps-new (8)
- # depstar (2)
- # etaoin (1)
- # fulcro (2)
- # funcool (2)
- # graalvm (5)
- # jackdaw (3)
- # java (17)
- # jobs-discuss (43)
- # kaocha (2)
- # leiningen (25)
- # malli (8)
- # minecraft (1)
- # missionary (8)
- # observability (6)
- # off-topic (37)
- # other-languages (12)
- # practicalli (1)
- # reagent (4)
- # releases (78)
- # remote-jobs (1)
- # sci (9)
- # shadow-cljs (13)
- # spacemacs (6)
- # sql (1)
- # tools-deps (30)
- # xtdb (3)
Hi, I was using fabricjs with clojure for some time, but it is bringing so many state and is written in a object oriented principals in mind. Is there anything like this library but done in more clojure/functional way? It doesn't have to be in clojure (although it would be great). Has someone here also use or used fabricjs with cljs?
I'm trying to wrap my head around using `this` in CLJS (if I should ever need that) by recreating this exercise:
const makeRandColor = () => {
const r = Math.floor(Math.random() * 255);
const g = Math.floor(Math.random() * 255);
const b = Math.floor(Math.random() * 255);
return `rgb(${r}, ${g}, ${b})`;
}
const buttons = document.querySelectorAll('button');
for (let button of buttons) {
button.addEventListener('click', colorize)
}
const h1s = document.querySelectorAll('h1');
for (let h1 of h1s) {
h1.addEventListener('click', colorize)
}
function colorize() {
this.style.backgroundColor = makeRandColor();
this.style.color = makeRandColor();
}
(ns weather.scratch
(:require [goog.dom :as gdom]))
(defn rand-rgb []
(rand-int 256))
(defn rand-color []
(let [[r g b] [(rand-rgb) (rand-rgb) (rand-rgb)]]
(str "rgb(" r ", " g ", " b ")")))
(defn change-background []
(let [new-color (rand-color)]
(set! (.. js/document -body -style -backgroundColor) new-color)))
(defn colorize []
(this-as this
(set! (.. this -style -color) (rand-color))
(set! (.. this -style -backgroundColor) (rand-color))))
(defn app []
[:div
[:h1 {:on-click colorize} "Hello"]
[:button {:on-click colorize} "Click Me"]])
I have also tried to have my `colorize` function take an element parameter like "button" and then access that but it doesn't work. My `change-background` function works to change the whole document but not sure how I can generically change elements as they are passed to the `colorize` function.
I am getting a "Cannot set properties undefined (setting 'color')" error when clicking.
The two examples are different in that the first is attaching the event handler directly to the element, where this
is bound to the event target (the element). In the second case, I guess you're using Reagent or some React wrapper, where the onClick
prop is passed through React's machinery and this
is bound to undefined. In both cases, a better way to get the element is through the event object which gets passed to the handler. :on-click (fn [event] (js/console.log (.-target event)))
(.addEventListener el (fn [event] (js/console.log (.-target event))))
This was my other attempt that isn't working either:
(defn colorize [element]
(let [el (gdom/getElement element)]
(set! (.. el -style -color) (rand-color))
(set! (.. el -style -backgroundColor) (rand-color))))
Ok, thanks @UE72GJS7J! I'll check this out.
I wouldn't use this
in this scenario 🙂 In general it's only used for object-oriented stuff. For event handlers, the event object contains everything you need
Got it. I'm still learning web dev in general (not just how to do cljs) and all these tutorials have this
all over the place. haha
This did work:
(defn colorize [event]
(let [el (.-target event)]
(set! (.. el -style -color) (rand-color))
(set! (.. el -style -backgroundColor) (rand-color))))
ty!It's kind of an old jQuery style to do things like <button onclick="$(this).foo()">
, but totally different in the React world 🙂 I recommend starting with React-specific tutorials if that's the way you want to go. There's a lot out there
hey all - I am looking to write a macro that includes a call to (Math/abs x)
. it is currently macroexpanding to (.abs java.lang.Math x)
… does anyone have advice on how I can have this appropriate expand for use in Clojurescript, to a call to (.abs js/Math x)
?
actually that is probably just what to do, write it like that vs trusting Math/abs
to expand…
In CLJ, (Math/abs x)
becomes (. Math abs x)
.
In CLJS, (Math/abs x)
remains that, but the compiler has a special case for Math
, so in the end it works just like (.abs js/Math x)
.
Given that macros expand during compile time, in CLJ, I don't think you can have plain (Math/abs x)
there - you have to make your macro conditional on whether it expands into CLJ or CLJS code.
As an alternative, I think, you can write your own abs
, put one copy into the CLJ file with the macros, put one in the corresponding CLJS file, and in the macro just use FQN for abs
.
it worked to use fork
from macrovich to do just what you suggested:
(defn- klein-term [acc delta]
`[sum# (+ ~acc ~delta)
~delta (if (ud/fork
:clj (>= (Math/abs ~(with-meta acc {:tag 'double}))
(Math/abs ~(with-meta delta {:tag 'double})))
:cljs (>= (.abs js/Math ~acc)
(.abs js/Math ~delta)))
(+ (- ~acc sum#) ~delta)
(+ (- ~delta sum#) ~acc))
~acc sum#])
ud/fork is the macrovich “fork”
Just in case - assuming the macro can accept something that's not just a number but rather a form, you definitely need to add (let [acc# ~acc] ...)
at the top and replace all usages of ~acc
with acc#
so that the same thing is not recomputed multiple times.
it’s private, and participating in generating entries for a big let binding (that’s enforced by the ~acc sum#
which will explode in the generated macro if acc
is not a symbol
but yes that is a great tip and I am glad to have it refreshed…
as an aside... Clojure 1.11 will add a fast polymorphic clojure.core/abs in the next alpha (and I expect CLJS will follow as well), kind of a fallout from https://clojure.atlassian.net/browse/CLJ-2673
and in general @U017QJZ9M7W you'll get best results for your stuff using the fns in the new clojure.math lib (which will also be cljs portable thanks to @U051N6TTC!)
One other item on my wishlist that I can't resist sharing is “transductions”, analogous to reductions. Stream of results of transducing successively longer prefixes of the input @U064X3EF3 . Or maybe something exists already?
I can build it out of reductions and sequence of course
does xforms have it?
https://github.com/cgrand/xforms/blob/master/src/net/cgrand/xforms.cljc#L491
Thanks for the reminder @U064X3EF3. There were some inlining issues that I wanted to fix. Done now 🙂
as a general tip for cljc macro cases I would recommend writing the macro to just call a function (eg. my-abs
) and then have that function handle the #?(:cljs ... :clj ...)
cases there. IMHO you should avoid host specific conditionals as much as possible in macros.
@U05224H0W I was groveling at the feet of the “Performance” gods, wanting that particular call in-lined…
here was the final macro: https://github.com/sicmutils/sicmutils/blob/main/src/sicmutils/algebra/fold.cljc#L471-L516
given how much boxed math is happening here (100x slower), inlining a call to abs is the least of your worries :)
oh wait, this is cljs, was referring to what I'd expect from this in jvm
Yeah, i need a major tutorial on detecting and eliminating boxing
In hot loop code…
well if you're using anything other than let and loop, you're probably using boxed math (any seq function, reduce, etc)
which is sad, because the code is usually a lot uglier
@U064X3EF3 of course you nerd-sniped me into writing a macro that pushed all of this into a loop/recur
(defn- klein-term
"Takes symbolic variables for
- `acc`, the accumulating term we're compensating for
- `delta`, the shared symbol used for deltas
and generates let-binding entries updating `acc` to `(+ acc delta)` and
`delta` to the new compensation amount in `(+ acc delta)`."
[acc delta]
`[sum# (+ ~acc ~delta)
~delta (if (ud/fork
:clj (>= (Math/abs ~acc)
(Math/abs ~acc))
:cljs (>= (.abs js/Math ~acc)
(.abs js/Math ~delta)))
(+ (- ~acc sum#) ~delta)
(+ (- ~delta sum#) ~acc))
~acc sum#])
(defmacro ^:no-doc kbk-n-sum
"Given some order `n`, generates a function implementing fast `n`-th order
Kahan-Babushka-Klein summation.
See [[kbk-n]] for more detail."
[n]
(let [syms (into [] (repeatedly (inc n) gensym))
zeros (map (fn [i] `(~'double ~i)) (repeat 0.0))
prefix (pop syms)
final (peek syms)
delta (gensym)]
`(fn [xs#]
(loop [i# (long 0)
~@(interleave syms zeros)]
(let [~delta (nth xs# i# nil)]
(if (not ~delta)
(+ ~@syms)
(let [~@(mapcat #(klein-term % delta) prefix)]
(recur (inc i#) ~@prefix (+ ~final ~delta)))))))))