This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-07-22
Channels
- # beginners (20)
- # boot (5)
- # cider (14)
- # cljs-dev (15)
- # cljsrn (1)
- # clojure (81)
- # clojure-greece (7)
- # clojure-italy (17)
- # clojure-spec (5)
- # clojure-uk (15)
- # clojurescript (143)
- # data-science (1)
- # datomic (7)
- # defnpodcast (4)
- # docs (1)
- # figwheel-main (1)
- # fulcro (37)
- # graphql (1)
- # hoplon (3)
- # luminus (1)
- # reitit (5)
- # shadow-cljs (10)
- # spacemacs (5)
- # tools-deps (14)
- # vim (7)
@aaron51, or only make additive changes, preserving backward compatibility š
@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
OK just tried vscode with Calva https://marketplace.visualstudio.com/items?itemName=cospaia.clojure4vscode and paredit
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
and now after checking out atom I can say that vscode calva provides a really great out of the box cljs experience
@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
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
Have to say Calva is getting to be one of my favorite as well, also nice cljs source compiled with shadow-cljs šŗ
what does it mean when a function has a ā-ā minus sign/dash before a function eg (-iterator [coll] (IndexedSeqIterator. arr i))
@lleo its convention for things you shouldn't call directly, sort of "private" or internal things. beyond that there is no special meaning
@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.
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.
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?
The cljs.core/eval
is really just there for self-hosted ClojureScript (if thatās what you are using)
From the big picture, are you wanting to write some code that dynamically loads some namespace?
Iāve figured out some of the behavior, and have an environment that does the right thingsā¦ except that I canāt require anything
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
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
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)
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 defn
s)
Yeah, the atom that you pass into cljs.js/eval-str
is the compiler state meant for self-hosted ClojureScript.
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.
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
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.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)
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
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.
Like I said, I used Chrisās method, where his macros searched the classpath at compile time
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
(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)))
but if I call:
(cljs.js/require {:*compiler* compiler-state}
namespace-name {:load loader} identity)
Then the loader does get calledI 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
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))
Perhaps @quoll you are passing a loader
that is nil
or undefined, while your real loader fn is something else?
Iām getting logging from my loader function, so no. And Iām referring to it directly, not with another var
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"}
before looking at that, Iām finding that my loader is not being called when evaluating that require form
Ahh, when you call (read-eval "(require '[clojure.set :refer [union]])")
your loader is not being calledā¦
Or you are referring to cljs.js/require
ā¦ Iām not super familiar with that one (usually just let cljs.js
call it internally
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
printing the :cljs.analyzer/namespaces
value from the compiler state returns just: (cljs.user cljs.core)
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])
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"}
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
Yeah, your loader should print the map
{:name clojure.set, :macros nil, :path "clojure/set"}
BTW, when you run it, what do you see afterwards for (keys (:cljs.analyzer/namespaces @compiler-state))
?
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)
ah, yeahā¦ OK. I forgot that we werenāt getting the loader to do anything (because for me, it does nothing anyway)
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
What happens is that it actually compiles the text "(ns clojure.set) (def x 1)"
in the JavaScript engine (this is self-hosted ClojureScript)
Yeah, if clojure.core.matrix
is self-host compatible this would work outā¦ checkingā¦
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])
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
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ā¦what if I do that again, and then:
(def array clojure.core.matrix/array)
(def reshape clojure.core.matrix/reshape)
Iāll see if it works. Itās a hack, but probably one I can live with, given the application
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]]
Thatās EXACTLY what I want to do. And weāve just spend the past hour failing to do that
I mean, I want to do it with a string that says ā(reshape (array (range 9)) [3 3])ā
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]])"
Butā¦ I wonder if clojure.core.matrix
is doing some funky macro stuff that isnāt self-host compatible
TBH, Iām getting loads of warnings when using clojure.core.matrix
from JVM ClojureScript, but it still seems to work in JVM ClojureScript
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)
clojure.core.matrix
doesnāt load in either Planck or Lumo. In both (which are self-hosted implementations) it fails to load.
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)
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.Then "(reshape (array (range 9)) [3 3])"
would work, leveraging the JavaScript code that had been loaded by JVM ClojureScripot
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)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
he prepended a 42 line definition with every call š https://github.com/ctford/klangmeister/blob/master/src/klangmeister/compile/eval.cljs#L87
Hmm. Maybe Chris does that to ensure that all of the Klangmeister symbols he wants are referred.
yes. Like me, itās allowing a user to type in ClojureScript code on a page, and get results from it
Yeah. A live didactic webpage teaching linear algebra based on ClojureScript sounds awesome.
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.
I havenāt been able to demonstrate any ability to require
/ :refer
in self-hosted ClojureScript
If you end up using :advanced
you might want to mess around with using ^:export
for the symbols you want available.
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.