Fork me on GitHub
#clojurescript
<
2020-06-16
>
Schpaa08:06:28

I have a vector of maps, with a field in it that I want to make the key of a new map with the all the original content. What’s the idiomatic way forward?

Schpaa08:06:36

(->> data 
     (map (fn [m] {(:yd m) m})) 
     (into {}))

delaguardo08:06:00

good place for using transducers:

(into {}
  (map (juxt :yd identity))
  data)

andy.fingerhut13:06:45

Maybe something like (group-by :map-field coll-of-maps) ?

😀 3
Schpaa10:06:22

thats what I ended up with 🙂

ikitommi08:06:18

still bit lost with dce + closure-defines. would like to make a registry function swappable, so that only one impl gets picked up and the rest get removed under advanced. Could use a string->function cond if I knew all the values in advance, but the users should be able to use their own registry-functions here too. I guess this needs som dynaload and macros to work?

(ns user)

;; registries drag code from 700b to 200kb, just need one for the app
(defn registry1 [] 1)
(defn registry2 [] 2)

;; user-defined name of the registry to be used
(goog-define type "user.registry2")

;; just taking one, dce removes the others, how to do this?
(def registry (some-> type symbol requiring-resolve (apply nil)))

thheller08:06:27

@ikitommi requiring-resolve doesn't exist. the only option you have that is supported by DCE is using strict identical? checks. for example https://github.com/clojure/clojurescript/blob/799d62fe3bba999287558538ca6b61501de02d49/src/main/cljs/cljs/core.cljs#L11747-L11753

thheller08:06:06

that won't work with user registry functions of course since it must be completely static

thheller08:06:34

the option I would suggest is having the user require whichever registry they want

thheller08:06:16

so a consumer does

(ns 
  (:require
    [your.registry.api :as reg]
    [your.regsitry.variant1]
    ))

thheller08:06:07

or if they want to able to swap it out more dynamically you can require it via the :entries in the build config in shadow-cljs. dunno how to do it in regular CLJS though given that :main only accepts a single symbol

thheller08:06:51

the variant1 just sets up whatever it has to directly using the api ns. eg. put things in an atom

thheller08:06:25

but "registry" in general is going to limit what DCE can do given that is most likely can't track which part of the registry you are actually going to use at runtime

thheller08:06:25

dynaload also doesn't really exist since CLJS can't do that. it requires the user requiring the ns just as described above

ikitommi08:06:38

I was hoping to get something like this:

(if (identical? type "default") 
  (default-registry)
  ...something-like-requiring-resolve-for-type...))
, where the else would be a macro to generate code to require and resolve the type. In my case, this is a default-registry. need to inject that into the library code as it is used in many places. I think either of the two would work: 1. define a boolean to indicate if the normal default registry should be used (if not, dce will remove it) and a programmatic way to once override the default registry via some set-default-registry! fn 2. just not have any default registry in cljs, one needs to always call set-default-registry!before use, could be in preloads was hoping a more declarative way for this, e.g. one :closure-defines definition…

ikitommi08:06:48

2 would be something like:

(require '[malli.core :as m])

;; always needed in cljs to get dce
(m/set-default-registry! (m/default-registry))

(m/validate [:map [:x int?] [:y int?]] {:x 1, :y 2})
; => true

(m/set-default-registry! (m/default-registry))
; => Exception, no mutable state here

thheller08:06:56

I always recommend all apps to have the generic init/start/stop lifecycle anyways https://code.thheller.com/blog/shadow-cljs/2019/08/25/hot-reload-in-clojurescript.html

thheller08:06:12

so that gives the user a chance to either init whatever registry they want in init or start

thheller08:06:20

initializing things in the ns automatically often creates problems with DCE if not done very carefully

thheller08:06:05

even the default registry setup means that the default registry is never removed if you put it into an atom or so

ikitommi08:06:12

thanks. a lot to think about. but good to know the dynamic resolve + dce won’t work. spent half a day trying to make it work, via JCM System Properties and all 🙂

thheller08:06:19

yeah you really can't do all the fun stuff you can do in clojure unfortunately

thheller08:06:24

not so much a CLJS problem but rather that the JS platform just doesn't allow it because of async IO

thheller09:06:33

but IMHO if you move each registry into its own ns and have it self-register on load is probably best

thheller09:06:42

that way the user can control which they want to use

thheller09:06:45

at least that pattern has reduced issues with DCE 😉

ikitommi08:06:04

but thanks again for the quick answers!

thheller08:06:06

its just not possible to do any kind of dynamic resolve. this would require doing async IO since we can't block to wait for the load result.

👌 3
Dennis Tel14:06:45

Hi! I’m considering Clojure/Clojurescript for a project. (Don’t have a lot of experience with it yet!) I need to create a library that has to run on both our backend (java/kotlin) and in the browser. Is it possible to compile the source to both JavaScript and a JAR? And are there other things to consider or are there any pitfalls?

noisesmith14:06:07

with cljc files you can provide conditional expressions that evaluate differently per back-end, they can be compiled by both the regular clojure compiler and the cljs compiler

noisesmith14:06:19

the conditionals are used for things that defer between clj / cljs

pez14:06:25

Clojure and ClojureScript are especially well suited for this. Most pitfalls I’ve been hitting has had to do with me not knowing the host platforms well enough. Both Clojure and ClojureScript run pretty close to the iron, so to speak. 😃

ghadi15:06:11

same conceptual language but different platform APIs

ghadi15:06:30

JDK API vs browser APIs

ghadi15:06:08

clojure core libraries are generally 80% the same

Dennis Tel13:06:06

I’ll probably only need the standard lib (mainly graph traversal etc) so that should be fine 😄 If I create a JS package from a ClojureScript library I’m assuming this also contains the standard library. Does the Closure compiler also dead-code-eliminate the unused parts of the stdlib?

noisesmith13:06:02

I do believe it will if you use advanced compilation

Dennis Tel13:06:37

awesome! Now a final question: is there a good leiningen template to get started with or should I just craete a new leiningen project and add clsjbuild?

noisesmith13:06:36

I am not sure what the current best lein template for clojurescript is, and I think a lot of the community there has moved to deps.edn

Dennis Tel13:06:20

So deps.edn does not work with leiningen?

noisesmith13:06:51

it's an alternative dep management system, there's at least one lein plugin to use deps.edn to manage deps inside leiningen though

noisesmith13:06:24

while lein tries to be a build tool, and a host for plugins for all clojure dev tasks, deps.edn only does dep management

Dennis Tel13:06:25

I’m still a n00b so for me i’m fine with whatever, just whichever is easiest or has the most learning materials around it 😄

noisesmith13:06:09

I think lein still has more documentation / ease of access. Just be aware that if you are looking for the "latest" or "best" version of something, deps.edn will come up

Dennis Tel13:06:51

thats good to know. I’m mostly concerned about the build tool as for the specific project I won’t need other dependencies other than maybe a test runner

frozar15:06:51

Hi I'm using shadow-cljs and I try to use a plain JS file. I don't get it right and try to understand why. I added the path "src/js" in the :source-paths of my shadow-cljs.edn file. In my src/main/simulation/core.cljs file, I have this header:

(ns simulation.core
  (:require
   [...]
   ["/d3/gravity" :as gravity]
   ))
I don't get the import error from the compiler so I guess the require statement works. In my src/js/d3/gravity.js file, I have just one function toto:
export default function toto() {
    console.log("INSIDE TOTO");
}
I cannot call toto from my simulation/core.cljs with this error message: > Failed to load simulation/core.cljs TypeError: module$d3$gravity.toto is not a function What is the right syntax for the function toto please? @thheller, if you're there, you're certainly the right one to ask ^^

thheller15:06:56

@fabien.rozar either ["/d3/gravity" :as gravity :default whatever] with whatever being the defaulft toto fn. or export function toto() { ...} and gravity/toto

frozar15:06:06

@thheller, nice thank you. The ["/d3/gravity" :as gravity] and export function toto() {...} and the call (gravity/toto) works fine for me. You're always there, amazing

💯 3
jpmonettas16:06:32

hi! what is the current way of using core.async in a cljc file? Trying

(ns mount.core
  #?(:clj  (:require [cljs.core.async :refer [<! chan go]]
                     ...)
     :cljs (:require [cljs.core.async :refer [<! chan go]]
                     ...)))
and compiling with the latest cljsbuild fails with clojure.lang.Compiler$CompilerException: java.lang.IllegalAccessError: <! does not exist, compiling:(mount/core.cljc:1:1) Any ideas?

lilactown16:06:48

@jpmonettas I think that the :clj branch should be (:require [clore.core.async :refer [<! chan go]] ...)

lilactown16:06:04

and for the :cljs branch you might need to use :include-macros true

jpmonettas16:06:07

@lilactown I tried some of those also, doing that it fails with : clojure.lang.ExceptionInfo: No such namespace: cljs.core.async.impl.protocols {:ns cljs.core.async.impl.protocols, :form cljs.core.async.impl.protocols/ReadPort, :file "mount/core.cljc" :column 23, :line 81}, compiling:(mount/core.cljc:79:3)

jpmonettas16:06:40

I have a typo

jpmonettas16:06:33

@lilactown hey that worked, thanks a lot! I think I tried the :refer-macros for cljs instead of the :include-macros and wasn't compiling

lilactown16:06:48

glad it worked!

dpsutton16:06:10

i don't believe :refer-macros should be needed

jpmonettas16:06:27

yeah, but shouldn't it also work?

reefersleep16:06:52

Is it possible to do something like (var-get #'my.ns/my-var) in Clojurescript?

noisesmith16:06:14

iirc cljs doesn't have vars at runtime(?)

reefersleep16:06:07

Maybe there’s a dirty way to do it? The function must be represented somehow in the global interpretation context 🙂

reefersleep16:06:14

(referrable, I mean)

reefersleep17:06:47

dev-local:cljs.user=> (def x "made it!")
#'cljs.user/x
dev-local:cljs.user=> (let [x 'cljs.user/x]
     @(resolve 'cljs.user/x))
"made it!"
Seems to work!

reefersleep17:06:43

Ah, but I can’t assign 'cljs.user/x to a different symbol, and then expect this to work. Dang.

thheller16:06:19

@jpmonettas just [clojure.core.async :refer [<! chan go]] is fine for both. no need for conditionals.

dpsutton16:06:41

yes it should also work. but no need for it. and cljs rewrites it like @thheller put there

jpmonettas16:06:33

it is also working with refer-macros so nvm, @thheller the conditionals are there for other requires

jpmonettas16:06:06

anyway it is working now, thx!

jacklombard18:06:55

Hello I am trying to integrate with a library and need to simulate classes in CLJS. I am using prototype extension to simulate them. But I have a very weird issue which is blocking me. Consider this minimum setup:

(defn class-one [])

(defn class-two []
  (this-as this
    (set! (.-some-property this) (new class-one))))

jacklombard18:06:09

Guess what is the result of (instance? class-two (new class-two)), it is false and (instance? class-one (new class-two)) is true

jacklombard18:06:27

any explanations for this behaviour?

jacklombard18:06:16

But if (set! (.-some-property this) (new class-one)) is something like (set! (.-some-property this) <something other than another class i.e. function>) then things work as expected

lilactown23:06:08

is there a way to clear out all cljs.test defs in a ns?

nickbauman01:06:55

with-redefs?

lilactown05:06:52

I want to delete tests. How would with-redefs help?