Fork me on GitHub
#clojurescript
<
2018-07-22
>
pesterhazy09:07:36

@aaron51, or only make additive changes, preserving backward compatibility šŸ™‚

martinklepsch14:07:23

@aaron51 I think ragtime is a migration library thatā€™s pretty agnostic to what kind of migrations itā€™s running. Might be worth looking at

bhauman16:07:04

it's a pretty good starter experience for ClojureScript and inline editing

bhauman16:07:58

now I'm going to take atom for a spin

jjttjj17:07:24

are any of the cljs react wrappers particularly well suited for interop with pre-made JS react components provided by a JS library? Or are they all fairly equal in this regard. Edit: actually after some initial research it seems like all the major cljs wrappers should be fine for what I need

bhauman17:07:00

and now after checking out atom I can say that vscode calva provides a really great out of the box cljs experience

justinlee17:07:53

@bhauman does atom or vscode provide things like inline doc strings and go-to-definition and things that cursive provides so well? i could never get them to work with cljs so i moved to cursive

bhauman17:07:34

oh I'm not comparing it to cursive or emacs

bhauman17:07:03

but that is a good question

bhauman17:07:32

they have years and years of development behind them

justinlee17:07:46

i just found it nigh impossible to get an nrepl connection from atom to work reliably and it seems that it needs that to do most of the fancy dynamic editor things

bhauman17:07:51

I'm just thinking about someone getting inline edit with less fuss

justinlee17:07:05

oh. whatā€™s ā€œinline editā€?

bhauman17:07:18

whoops inline eval

bhauman17:07:03

vscode calva does a very good job connecting and differentiating between buffers

richiardiandrea17:07:24

Have to say Calva is getting to be one of my favorite as well, also nice cljs source compiled with shadow-cljs šŸ˜ŗ

lleo19:07:07

what does it mean when a function has a ā€œ-ā€ minus sign/dash before a function eg (-iterator [coll] (IndexedSeqIterator. arr i))

thheller20:07:20

@lleo its convention for things you shouldn't call directly, sort of "private" or internal things. beyond that there is no special meaning

lleo20:07:29

interestingā€¦ Thanks

mfikes20:07:38

@lleo FWIW, in Clojure Inside Out Stuart Halloway explains that to avoid name collisions, it became idiomatic in ClojureScript to name protocol functions this way, and that this doesnā€™t semantically convey anything; itā€™s just convention.

dnolen21:07:00

one useful thing (I guess pattern really) is that protocol fn implementers can handle the details - and a fn name without the dash can do shared behavior, validation, etc.

dnolen21:07:17

i.e. first vs -first

quoll21:07:25

Iā€™m wondering if anyone is familiar with how to do a ā€œrequiresā€ in a ClojureScript eval please? Trying to eval-str (requires '[lib-name :refer [some-fn]]) results in nothing being found, even with a loader available. (Loader copied from Chris Fordā€™s Klangmeister). I had more luck when using cljs.js/require. In this case, it actually updates the :cljs.analyzer/namespaces field in the compiler state, and even brings in some of the dependencies, so itā€™s not just adding names. But despite this, I am still unable to reference functions in those namespaces. Are there tricks to this that Iā€™m too naĆÆve to understand? Are there examples where others have done this?

mfikes22:07:55

@quoll Are you using self-hosted ClojureScript?

quoll22:07:13

Iā€™m in Figwheel

mfikes22:07:35

The cljs.core/eval is really just there for self-hosted ClojureScript (if thatā€™s what you are using)

quoll22:07:55

Iā€™m using cljs.js/eval-str

mfikes22:07:10

Ahh, cljs.js/eval-str is self-hosted ClojureScript.

quoll22:07:24

(I started with cljs.js/eval, but decided that I didnā€™t need to read it for myself)

mfikes22:07:16

From the big picture, are you wanting to write some code that dynamically loads some namespace?

quoll22:07:23

Iā€™ve figured out some of the behavior, and have an environment that does the right thingsā€¦ except that I canā€™t require anything

quoll22:07:11

Actuallyā€¦ no. I know which namespaces are needed. I even know the functions in the namespaces that I need. I just need access to those functions with local names rather than fully qualified names

quoll22:07:57

I found that if the cljs file requires the namespace, and then I refer to a function with its fully qualified name in the evalā€™ed text, then it will work

quoll22:07:14

butā€¦ I really want the short names

mfikes22:07:33

Part of what is going on is that, when using cljs.js/eval-str, it ends up manipulating some compiler state that is maintained in the JavaScript engine. (This differs from the usual compiler state that is maintained in Clojure / JVM for conventional ClojureScript)

quoll22:07:09

this is the atom thatā€™s passed in?

quoll22:07:20

or are you talking about some other state?

mfikes22:07:04

So, I suspect that cljs.js/eval-str will effectively cause some ClojureScript code to be loaded, compiled, and therefore make changes to the JavaScript environment (for example making new JavaScript functions available, associated with defns)

mfikes22:07:28

Yeah, the atom that you pass into cljs.js/eval-str is the compiler state meant for self-hosted ClojureScript.

quoll22:07:43

this is the state that I was referring to when I said that evalā€™ing a requires form did nothing to update the :cljs.analyzer/namespaces part of the state.

quoll22:07:15

However, calling cljs.js/requires does update this part of the state

mfikes22:07:55

Here is an example that may shed some light:

cljs.user=> (require 'cljs.js)
nil
cljs.user=> (def state (cljs.js/empty-state))
#'cljs.user/state
cljs.user=> (cljs.js/eval-str state "(defn foo [x] (inc x))" nil {:eval cljs.js/js-eval :context :expr} prn)
{:ns cljs.user, :value #object[cljs$user$foo]}
nil
cljs.user=> (cljs.user/foo 3)
WARNING: Use of undeclared Var cljs.user/foo at line 1 <cljs repl>
4

quoll22:07:08

hmmm, no, thatā€™s not what Iā€™m looking at

mfikes22:07:35

After having done that,

(get-in @state [:cljs.analyzer/namespaces 'cljs.user :defs])
will show that foo is known in that state atom. Butā€¦ there is another state atom in the JVM that is separate and thatā€™s wy the WARNING appears, even though the fn call suceeds.

mfikes22:07:12

So the above is just to illustrate the idea that there is a JVM compiler state that is separate from self-hosted compiler state (which lives in the JavaScript engine)

mfikes22:07:52

cljs.js/require is meant to be used with self-hosted ClojureScript

mfikes22:07:57

(Chris Fordā€™s Klangmeister is using self-hosted ClojureScript)

quoll22:07:34

Here is what I tried to do in the first place:

cljs.user=> (require 'cljs.js)
nil
cljs.user=> (def state (cljs.js/empty-state))
#'cljs.user/state
cljs.user=> (cljs.js/eval-str state "(require '[clojure.set :refer [union]])" nil {:eval cljs.js/js-eval :context :expr} prn)
{:error #error {:message "Could not require cljs.set", :data {:tag :cljs/analysis-error}, :cause #object[Error Error: No *load-fn* set]}}
nil
cljs.user=> (cljs.js/eval-str state "(union #{1 2} #{2 3}" nil {:eval cljs.js/js-eval :context :expr} prn)
{:error #error {:message "ERROR", :data {:tag :cljs/analysis-error}, :cause #object[TypeError TypeError: Cannot read property 'union' of undefined]}}
nil

mfikes22:07:16

OK, yeah, to make that work, a load function needs to be defined for self-hosted ClojureScript, and in this case, the load functionā€™s job would be to respond by calling back with the source for the clojure.set ClojureScript namespace.

quoll22:07:44

and I have done that

quoll22:07:36

Like I said, I used Chrisā€™s method, where his macros searched the classpath at compile time

mfikes22:07:54

OK, then in that map you are passing in to cljs.js/eval-str, you would also need to pass a :load key where the value is your load fn

quoll22:07:05

yes, I have that

quoll22:07:21

again, a call to require does nothing

mfikes22:07:30

OK. I donā€™t see it above. All I see is {:eval cljs.js/js-eval :context :expr}

mfikes22:07:50

Followed by the error regarding no *load-fn*

quoll22:07:53

that was because I was trying to show you in the context of what you said, but OK

mfikes22:07:40

Does your load fn get called?

quoll22:07:11

(defn read-eval
  [s]
  (let [x (atom nil)]
    (cljs.js/eval-str compiler-state s nil
                      {:eval cljs.js/js-eval
                       :load loader
                       :verbose true}
                      #(reset! x (:value %)))
    (deref x)))

quoll22:07:21

No, the load function is not called

quoll22:07:13

but if I call:

(cljs.js/require {:*compiler* compiler-state}
                   namespace-name {:load loader} identity)
Then the loader does get called

quoll22:07:23

and compiler-state gets updated

quoll22:07:05

I donā€™t know if it gets updated enough (maybe something is missing?) but it changes, and the :cljs.analyzer/namespaces field has the required namespaces appended

mfikes22:07:48

When I call (read-eval "(require '[clojure.set :refer [union]])"), for me loader gets called with

({:name clojure.set, :macros nil, :path "clojure/set"} #object[Function])
where loader is defined as
(defn loader [& args] (prn args))

mfikes22:07:19

If I change loader to be nil, then I get the Error: No *load-fn* set

mfikes22:07:59

Perhaps @quoll you are passing a loader that is nil or undefined, while your real loader fn is something else?

quoll22:07:40

Iā€™m getting logging from my loader function, so no. And Iā€™m referring to it directly, not with another var

mfikes22:07:04

Ahh, OK, so if your loader is being called, are you calling back on the cb, passing something like

{:lang :clj
 :source ".... the source for the clojure.set namespace"}

quoll22:07:56

before looking at that, Iā€™m finding that my loader is not being called when evaluating that require form

mfikes22:07:18

Ahh, when you call (read-eval "(require '[clojure.set :refer [union]])") your loader is not being calledā€¦

mfikes22:07:24

Or you are referring to cljs.js/requireā€¦ Iā€™m not super familiar with that one (usually just let cljs.js call it internally

quoll22:07:56

the first. Calling read-eval on a require form is not calling the loader

quoll22:07:26

Iā€™m slow, because I cleared everything out, and reinitialized everything, and tried it all again with a clean implementation for read-eval and loader

quoll22:07:46

and there is no change when the require form is run

quoll22:07:53

printing the :cljs.analyzer/namespaces value from the compiler state returns just: (cljs.user cljs.core)

mfikes22:07:33

I get this

cljs.user=> (cljs.js/require {:*compiler* compiler-state}
 'clojure.set {:load loader} identity)
({:name clojure.set, :macros nil, :path "clojure/set"} #object[Function])

mfikes22:07:53

(Where loader is (defn loader [& args] (prn args)))

mfikes22:07:52

That last argument (the #object[Function]) is the callback, and loader should call it with the map

{:lang :clj
 :source ".... the source for the clojure.set namespace"}

quoll22:07:14

OK, so Iā€™ve just done this twice, and itā€™s not operating as I think it is for you. Can you see where we differ?

(require 'cljs.js)
(def compiler-state (cljs.js/empty-state))
(defn loader [args c] (println args))
(defn read-eval
  [s]
  (let [x (atom nil)]
    (cljs.js/eval-str compiler-state s nil
                              {:eval cljs.js/js-eval
                               :load loader}
                            #(reset! x (:value %)))
    (deref x)))

=> (read-eval "(require '[clojure.set :refer [union]])")
nil

quoll22:07:29

my loader is defined differently, but it should still work, right?

mfikes22:07:11

Yeah, your loader should print the map

{:name clojure.set, :macros nil, :path "clojure/set"}

quoll22:07:06

BTW, when you run it, what do you see afterwards for (keys (:cljs.analyzer/namespaces @compiler-state)) ?

mfikes22:07:34

Just (cljs.user cljs.core), because the c argument is not being called back upon

quoll22:07:28

I donā€™t follow what you mean by the c argument

mfikes22:07:38

For example if you define loader like this

(defn loader [args c] (c {:lang :clj :source "(ns clojure.set) (def x 1)"}))
then you will ultimately see this
cljs.user=> (keys (:cljs.analyzer/namespaces @compiler-state))
(cljs.user cljs.core clojure.set)

mfikes22:07:01

In this definition of loader, c is being called.

mfikes22:07:33

(With bogus source for the clojure.set namespace.)

quoll22:07:41

ah, yeahā€¦ OK. I forgot that we werenā€™t getting the loader to do anything (because for me, it does nothing anyway)

quoll22:07:55

so you know, Iā€™m not actually doing any of this with clojure.set. I just thought that was convenient for illustrative purposes. Iā€™m actually attempting to load clojure.core.matrix

mfikes23:07:10

What happens is that it actually compiles the text "(ns clojure.set) (def x 1)" in the JavaScript engine (this is self-hosted ClojureScript)

mfikes23:07:50

Yeah, if clojure.core.matrix is self-host compatible this would work outā€¦ checkingā€¦

quoll23:07:52

well, Iā€™ve been able to get to a point of executing this using read-eval:

(clojure.core.matrix/reshape (clojure.core.matrix/array (range 9)) [3 3])

quoll23:07:03

worked perfectly

quoll23:07:24

but I want to get to:

(reshape (array (range 9)) [3 3])

mfikes23:07:47

Prognosis doesnā€™t look good for self-hosted ClojureScript

$ plk -Sdeps '{:deps {net.mikera/core.matrix {:mvn/version "0.62.0"}}}'
Downloading: net/mikera/core.matrix/0.62.0/core.matrix-0.62.0.pom from 
Downloading: net/mikera/clojure-pom/0.6.0/clojure-pom-0.6.0.pom from 
Downloading: net/mikera/mikera-pom/0.6.0/mikera-pom-0.6.0.pom from 
Downloading: net/mikera/core.matrix/0.62.0/core.matrix-0.62.0.jar from 
ClojureScript 0.0.2103577952
cljs.user=> (require 'clojure.core.matrix)
WARNING: Use of undeclared Var clojure.core.matrix.utils/x at line 151 clojure/core/matrix/utils.cljc
WARNING: Use of undeclared Var clojure.core.matrix.utils/i at line 151 clojure/core/matrix/utils.cljc
WARNING: Use of undeclared Var clojure.core.matrix.utils/i at line 151 clojure/core/matrix/utils.cljc
WARNING: Use of undeclared Var clojure.core.matrix.utils/x at line 151 clojure/core/matrix/utils.cljc
WARNING: Use of undeclared Var clojure.core.matrix.utils/x at line 170 clojure/core/matrix/utils.cljc
WARNING: Use of undeclared Var clojure.core.matrix.utils/i at line 170 clojure/core/matrix/utils.cljc
WARNING: Use of undeclared Var clojure.core.matrix.utils/i at line 170 clojure/core/matrix/utils.cljc
WARNING: Use of undeclared Var clojure.core.matrix.utils/x at line 170 clojure/core/matrix/utils.cljc
WARNING: Use of undeclared Var clojure.core.matrix.impl.common/ClassCastException at line 55 clojure/core/matrix/impl/common.cljc
WARNING: Use of undeclared Var clojure.core.matrix.impl.persistent-vector$macros/vector-1d? at line 537 clojure/core/matrix/impl/persistent_vector.cljc
No such namespace: cljs.core.matrix, could not locate cljs/core/matrix.cljs, cljs/core/matrix.cljc, or JavaScript source providing "cljs.core.matrix" in file clojure/core/matrix/impl/persistent_vector.cljc

mfikes23:07:47

I wonder if your

(clojure.core.matrix/reshape (clojure.core.matrix/array (range 9)) [3 3])
worked because you have that lib loaded in JVM ClojureScriptā€¦

quoll23:07:01

yes, Iā€™m thinking so

quoll23:07:04

what if I do that again, and then:

(def array clojure.core.matrix/array)
(def reshape clojure.core.matrix/reshape)

quoll23:07:35

Iā€™ll see if it works. Itā€™s a hack, but probably one I can live with, given the application

quoll23:07:32

OK, that works

quoll23:07:42

I hate it, but Iā€™ve been hours on this šŸ™‚

mfikes23:07:51

You donā€™t want to just refer the symbols?

$ clj -Sdeps '{:deps {net.mikera/core.matrix {:mvn/version "0.62.0"} org.clojure/clojurescript {:mvn/version "1.10.339"}}}' -m cljs.main -re node -r
ClojureScript 1.10.339
cljs.user=> (require '[clojure.core.matrix :refer [array reshape]])
...
nil
cljs.user=> (reshape (array (range 9)) [3 3])
[[0 1 2] [3 4 5] [6 7 8]]

quoll23:07:50

Thatā€™s EXACTLY what I want to do. And weā€™ve just spend the past hour failing to do that

mfikes23:07:58

Hah! šŸ™‚

mfikes23:07:20

Leave self-hosted ClojureScript alone if you want to refer the symbols. šŸ™‚

quoll23:07:29

I mean, I want to do it with a string that says ā€œ(reshape (array (range 9)) [3 3])ā€

quoll23:07:42

yeah, thatā€™s not a particularly helpful suggestion here

mfikes23:07:00

Ohā€¦ ok, so you do want to evaluate that at runtime using cljs.js/eval-str

mfikes23:07:40

So, that would indeed involve self-hosted ClojureScript, and it would ideally just involve first evaluating the string "(require '[clojure.core.matrix :refer [array reshape]])"

quoll23:07:02

which was how I got into implementing loaders

mfikes23:07:11

Butā€¦ I wonder if clojure.core.matrix is doing some funky macro stuff that isnā€™t self-host compatible

mfikes23:07:00

TBH, Iā€™m getting loads of warnings when using clojure.core.matrix from JVM ClojureScript, but it still seems to work in JVM ClojureScript

quoll23:07:51

I donā€™t know. When I tried that require form my loader was never called. Using cljs.js/require does call the loader, and then I saw the result of (keys (:cljs.analyzer/namespaces @compiler-state)) included:

(cljs.user cljs.core clojure.core.matrix clojure.core.matrix.impl.defaults cljs.core.matrix.impl.defaults)

quoll23:07:20

however, I still couldnā€™t refer to anything in the clojure.core.matrix namespace

mfikes23:07:45

clojure.core.matrix doesnā€™t load in either Planck or Lumo. In both (which are self-hosted implementations) it fails to load.

quoll23:07:23

Every bit of ClojureScript code Iā€™ve ever built gives me lots of warnings šŸ™‚ (usually warnings that the ->RecordName vars are called but not defined)

mfikes23:07:23

Hah. If you are loading clojure.core.matrix into your ā€œambientā€ JVM-based environment, perhaps, just to evaluate the strings you could do

(def array clojure.core.matrix/array)
(def reshape clojure.core.matrix/reshape)
via cljs.js/eval-str to set up those aliases.

mfikes23:07:02

Then "(reshape (array (range 9)) [3 3])" would work, leveraging the JavaScript code that had been loaded by JVM ClojureScripot

mfikes23:07:36

If self-hosted ends up emitting warnings about clojure.core.matrix/array being an undeclared var, that def could be written

(def array (.. js/clojure -core -matrix -array))
(and likewise for the other)

quoll23:07:19

There was one thing that Chris was doing. I guess I could try it. Basically, he prepended a large ns form in front of the text he was evaluating every time he called cljs.js/eval-str

quoll23:07:46

but like you say, maybe core.matrix wonā€™t like this environment

mfikes23:07:45

Hmm. Maybe Chris does that to ensure that all of the Klangmeister symbols he wants are referred.

quoll23:07:07

yes. Like me, itā€™s allowing a user to type in ClojureScript code on a page, and get results from it

mfikes23:07:40

Yeah. A live didactic webpage teaching linear algebra based on ClojureScript sounds awesome.

mfikes23:07:25

Another route is to define a simple ā€œbridgeā€ namespace that can be loaded in ClojureScript that defines all of the desired symbols with things like (def array (.. js/clojure -core -matrix -array)) and then in self-hosted require and :refer the symbols from that bridge namespace.

quoll23:07:45

I havenā€™t been able to demonstrate any ability to require / :refer in self-hosted ClojureScript

mfikes23:07:10

If you end up using :advanced you might want to mess around with using ^:export for the symbols you want available.

mfikes23:07:21

If you use a simple bridge namespace, then it makes it easier to make require / :refer work because your loader function can pre-bake the source for that simple bridge namespace.

mfikes23:07:55

But, certainly (def array clojure.core.matrix/array) works

quoll23:07:21

what I can say at this point is that because I canā€™t get any version of require to work for me, then I donā€™t see that itā€™s worth the effort to invest in that path