clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
Oliver George 2021-05-14T02:55:48.433100Z

Can anyone recommend a simple way to evalue a user generated expression within my CLJS app (e.g. eval but sandboxed). An interpreter is fine really - not super worried about performance. I see references to cljs.js and also borkdude/sci which sound like candidates. Not familiar with pros/cons of different approaches.

Oliver George 2021-05-14T02:55:48.433300Z

Use case is to allow user configured behaviour of single page webapp without recompiling frontend. (e.g. setting component properties based on simple formula)

dnolen 2021-05-14T02:58:57.433600Z

cljs.js is not sandboxed at all

dnolen 2021-05-14T03:00:11.434Z

if the language is very restricted I would probably just consider writing your own interpreter

borkdude 2021-05-14T06:47:05.435700Z

@olivergeorge yes, that is one of the use cases of sci. Itโ€™s also used by Chlorine/Clover for configuration

Oliver George 2021-05-14T07:19:17.436Z

Thanks guys

Endre Bakken Stovner 2021-05-14T15:31:08.439Z

How do I use react from cljs? I'd like to use react/useEffect, but importing react with ["react" :as react] ;; shadow-cljs gives me this error in the developer console:

env.cljs:198 error when calling lifecycle function mount-components Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
I'm guessing the reason is point 3 here since I am likely importing react a second time. The code example I am trying to get to work is taken from https://juxt.pro/blog/react-hooks-raw#_side_effects (but using js/React.useEffect like in that example did not work).

dpsutton 2021-05-14T15:34:19.440Z

are you using useEffect inside of a defn?

Endre Bakken Stovner 2021-05-14T15:34:26.440200Z

Yes.

lilactown 2021-05-14T15:35:35.440500Z

@endrebak85 how are you rendering the function component that calls useEffect?

lilactown 2021-05-14T15:35:43.440800Z

are you using a React wrapper like reagent?

Endre Bakken Stovner 2021-05-14T15:36:34.441500Z

I am rendering it in hiccup like this: [render-function-from-example].

Endre Bakken Stovner 2021-05-14T15:36:50.441900Z

I am using reagent in my code, yes ๐Ÿ™‚

lilactown 2021-05-14T15:37:05.442300Z

you cannot use React hooks inside normal reagent components

Endre Bakken Stovner 2021-05-14T15:37:16.442800Z

Understood, thanks.

dpsutton 2021-05-14T15:37:26.443300Z

those get turned into classes. and the warning "Hooks can only be called inside of the body of a function component" speaks right at that

11๐Ÿ™
lilactown 2021-05-14T15:38:09.443800Z

here's how to use React hooks in reagent: https://github.com/reagent-project/reagent/blob/master/doc/ReactFeatures.md#hooks

lilactown 2021-05-14T15:38:57.444600Z

if you're using reagent 1.0 or above, you can render your component like:

[:f> render-function-from-example]

Endre Bakken Stovner 2021-05-14T15:43:29.444900Z

Thanks, now it works :)

Endre Bakken Stovner 2021-05-14T17:01:41.452Z

I was advised to use useEffect to be able to update DOM elements with D3 graphs after the DOM elements had rendered. I think I am close, but that I am making a mistake here:

(defn graph-page []
  (let [[graph set-graph]
        (react/useState ;; the state is a dagre graph
         (-> (dagre/graphlib.Graph.)
             (.setGraph (clj->js {}))
             (.setDefaultEdgeLabel (fn [] {}))
             (.setNode "hi" (clj->js {:label "hi" :width 144 :height 100}))
             (.setNode "ciao" (clj->js {:label "ciao" :width 144 :height 100}))
             (.setEdge "hi" "ciao")))
        render-graph (fn [graph] ;; this renders the graph with dagre-d3
                       (js/console.log "render-graph is executed!")
                       (let [renderer (dagre-d3/render)
                             svg (d3/select "svg")
                             inner (-> svg
                                       (.append "g"))
                             _ (renderer inner graph)]))]
    [:div
     [:svg]
     [:button {:on-click #(set-graph (render-graph %))} "Graph!"]]))
Is this correct? Clicking the button just adds an empty <g></g> to the DOM (from render-graph). I am wondering whether the error is in my use of hooks, or my use of the dagre-d3 library (proper JS-usage of the code I am trying to write use in Clojurescript is https://dagrejs.github.io/project/dagre-d3/latest/demo/sentence-tokenization.html). One potential problem with my code is that graph is not something that should be included in the hiccup, but it the graph that should be rendered.

cassiel 2021-05-14T17:05:59.453800Z

A most trivial item of trivia, but: I was rather surprised to find that defprotocol evaluates to false. I had a quick look at the GitHub docs and the last line of the defprotocol macro is a (set! ~'*unchecked-if* false), so I guess thatโ€™s it. What should defprotocol evaluate to?

Endre Bakken Stovner 2021-05-14T17:06:31.454Z

With [:button {:on-click #(set-graph (render-graph graph))} "Graph!"] it renders.

p-himik 2021-05-14T17:10:01.454200Z

I didn't see it described anywhere, but FWIW Clojure explicitly returns the symbol for the protocol. So CLJ and CLJS behavior here is different. But if the behavior itself is not described anywhere, then it's probably fine.

2021-05-14T17:10:53.454400Z

I believe most programs completely ignore the value returned from such expressions.

lilactown 2021-05-14T17:10:58.454600Z

(render-graph %) would pass the button event to the render-graph fn which doesn't seem right

lilactown 2021-05-14T17:11:26.454800Z

your change seems correct

Endre Bakken Stovner 2021-05-14T17:11:54.455Z

But is not my code equivalent to the one from the reagent example?

(defn example []
  (let [[count set-count] (react/useState 0)]
    [:div
     [:p "You clicked " count " times"]
     [:button
      {:on-click #(set-count inc)}
      "Click"]])) 

lilactown 2021-05-14T17:13:00.455400Z

[:button {:on-click #(set-graph (render-graph %))} "Graph!"]
expand this to:
[:button {:on-click (fn [button-event]
                      (render-graph button-event))} "Graph!"]

lilactown 2021-05-14T17:13:14.455600Z

why would you pass the DOM button-event to render-graph?

lilactown 2021-05-14T17:13:20.455800Z

it expects a graph!

lilactown 2021-05-14T17:14:07.456Z

#(set-count inc) passes inc to set-count, not the button-event too

Endre Bakken Stovner 2021-05-14T17:14:23.456200Z

Ah, I see. Thanks for your patience.

dnolen 2021-05-14T17:24:18.457100Z

@cassiel that expression is really a hint for the compiler

cassiel 2021-05-14T17:26:12.457200Z

True enough - but seeing false when I mash a defprotocol in Emacs is disconcerting.

2021-05-14T17:27:04.457500Z

You are welcome to add a question on http://ask.clojure.org about this if it disconcerts you enough. Realize that it may not disconcert the ClojureScript maintainers as it does you. I do not know.

cassiel 2021-05-14T17:27:59.457800Z

Iโ€™ll think about how disconcerted I am.

rafalw 2021-05-14T18:05:43.458400Z

hi, @dnolen https://www.reddit.com/r/Clojure/comments/mcce4h/everything_i_have_learned_i_have_learned_from/ "29:36 - is source code for this available anywhere ?"

alexmiller 2021-05-14T18:07:14.458800Z

would ask @dnolen

dnolen 2021-05-14T18:17:41.459200Z

@rafal.wysocki in core.match

1โค๏ธ
dnolen 2021-05-14T18:18:26.460300Z

funny story I think someone from Apple was there and at least some of that paper ended up in the Swift pattern matcher?

1๐Ÿ’ฅ
Pepijn de Vos 2021-05-14T18:52:42.462Z

Also funny story: I shared the core.match literature with someone which almost turned into a thing to optimize state machines in Yosys, but I don't think it was ever completed.

camsaul 2021-05-14T22:29:16.469600Z

Is there a way to tell if we're in the Clojure cljs macroexpansion stage vs just regular Clojure? Here's my motivation: I have a log util namespace util.clj with macros like

(defmacro logp
  "Impl for log macros. Log `args` at `level`."
  [level & args]
  (macros/case
    :clj
    `(clojure.tools.logging/logp ~level ~@args)

    :cljs
    `(js-logp ~level ~@args)))
(I'm using https://github.com/cgrand/macrovich here). When expanding a macro for CLJS compliation, I want js-logp. When expanding a macro for Clojure (in normal usage), I want clojure.tools.logging. My issue is one of may namespaces that gets loaded during the Clojure cljs macroexpansion stage does some logging as a side-effect of being loaded. So during cljs macroexpansion it's expanding the macro as clojure.tools.logging.logp in Clojure land in order to do the macroexpansion. However, I don't have clojure.tools.logging as a dependency in that situation, so the macroexpansion is causing my Clojurescript build to fail. (This code is also used elsewhere as a sub-project where clojure.tools.logging is available.) I want to do something like
(defmacro logp
  "Impl for log macros. Log `args` at `level`."
  [level & args]
  (when-not *clojurescript-macroexpansion*
    (macros/case
      :clj
      `(clojure.tools.logging/logp ~level ~@args)

      :cljs
      `(js-logp ~level ~@args))))
and have the macro expand to nil or some other sort of no-op if we're expanding it while running in Clojure to do ClojureScript macro expansion. Is there some way I can possibly figure out if the code is running in "Clojure for ClojureScript macroexpansion mode"?

lsenjov 2021-05-16T23:50:01.023400Z

Found this recently while digging, I think it's what you're after https://github.com/plumatic/schema/blob/master/src/clj/schema/macros.clj#L10

1๐Ÿ‘
2021-05-19T14:51:12.047900Z

I could be wrong but I think this is what deftime (from Macrovich) is for

1๐Ÿ‘