Fork me on GitHub
#clojurescript
<
2020-01-28
>
Varenya06:01:23

okay may be a silly question i am using secretary for routing and want to know what the if a particular link is active or not is there a straightforward way to do it?

borkdude08:01:38

@varen90 You can store the current route in some atom or if you're using re-frame, the re-frame db

Varenya09:01:15

yeah we can but i was hoping for a more native solution

Hi11:01:35

how to get current ns in a function? I am not sure if *ns* works outside REPL

borkdude11:01:20

@feikas If *ns* doesn't work, you can maybe try to use the metadata on vars: (ns foo) (def x) (:ns (meta (var x))), although I'm not sure if that works with (advanced) compiled code...

borkdude11:01:22

it does seem to work with advanced compilation

borkdude11:01:49

tried this:

clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.597"}}}' -m cljs.main -t node -O advanced -c foo.core
with foo.core being the code above

borkdude11:01:01

and then node out/main.js

thheller11:01:52

there is no such thing as a "current ns" in a function. that only exists at compile time/macros. do not go with var, that adds way too much "noise" into your code.

thheller11:01:55

if you really must have it for some reason you can do (namespace ::foo)

dazld11:01:02

@thheller I guess that explains why resolve is a macro in cljs?

thheller11:01:00

resolve is a macro because the analyzer data is not available at runtime like in clojure, thus you can't access it dynamically

👍 4
borkdude11:01:03

hmm, this seems to work:

(ns foo.core
  #?(:cljs (:require-macros [foo.core :refer [curr-ns]])))

(defmacro curr-ns []
  `'~(ns-name *ns*))

(defn foo []
  (prn (curr-ns)))

(foo) ;; prints foo.core

thheller11:01:31

yes, thats a macro. thats works fine because at compile time *ns* is bound to whatever namespace is being compiled

borkdude11:01:49

so that might work in @feikas's case

4
dazld11:01:14

looks like it! while we’re talking about this, there’s no way to dynamically resolve a var at runtime in cljs?

dazld11:01:24

if I pass it as data, for example

dazld11:01:02

the use case I have is a cross platform library, that in clj can take quoted args

dazld11:01:15

will have to think of another way to do it then

dazld11:01:17

maybe it doesn’t need to be 100% dynamic..

borkdude11:01:38

@dazld what kind of lib is it?

dazld11:01:49

it’s a wip, one moment

dazld11:01:31

it’s based on EQL

dazld11:01:43

so, parameters that might include a var

dazld11:01:06

sorry, that PR isn’t particularly clean

dazld11:01:45

in this case, that’s probably fixable as your suggestion previously, as it’s not dynamic as such

dazld11:01:18

so, making it a macro would let it resolve that stuff at compile time. would be nice to make it be able to take different kind of queries at runtime though

Kevin12:01:06

Is it in any way possible to use require inside a defn? For example:

(defn do-something []
  (require 'some.cljs.namespace)
  (do-something-else))

Kevin12:01:02

Why I would want this: There's a piece of code in the Integrant library that's Clojure specific. Because it uses a require within a defn https://github.com/weavejester/integrant/blob/ca8d6327708cf291f2f310d476124271fcbe0b89/src/integrant/core.cljc#L186-L200 I was wondering if it was possible to make this possible for ClojureScript.

Kevin12:01:31

Thought so, just wanted to ask here to make sure

andrewboltachev13:01:29

Hello! Does anyone know the way of returning JS literal like #js {:a 1 :b 2} from a macro? I'm getting *No reader function for tag js* exception

thheller13:01:38

@andrewboltachev you can use ~(cljs.tagged-literals/read-js {:a 1 :b 2}) or construct JSValue directly if you want to skip the validation

andrewboltachev14:01:07

@thheller ah, the JSValue. I somehow thought it's only for parsing, so thanks!

darwin19:01:44

writing a macro and I would like to have a helper function which would tell me that given value can be emitted as primitive cljs type as-is wondering if something like this already exists somewhere…

thheller19:01:01

don't think there is a function for this but you are missing nil?, boolean? and symbol?

J21:01:59

Hey 🙂! Is anyone trying to work with shadow-cljs, react-native-web and ui-kitten without expo ?

darwin22:01:45

I have a macro which is processing a map, I’m generating cljs code based on visited keys and values. My issue is that I need to generate the code in the same order as individual keys/vals appeared in the source code. Naively iterating over a map value in macro seem to not preserve the order. Any ideas?

darwin22:01:08

btw. I’m using reduce over the map’s [k v]

Alex Miller (Clojure team)22:01:54

so this isn't going to work with a map as is

darwin22:01:06

yep, looks like I’m toasted

Alex Miller (Clojure team)22:01:34

if you look at something like let , the use of a vector for bindings enforces a semantic order

darwin22:01:50

thanks for the suggestion, this is part of an effort to translate react props passed as cljs map to js conventions (at compile time), not sure if this a-vector-instead-of-a-map proposal would be palatable in this case

lilactown22:01:27

@darwin curious about the necessity for preserving order

darwin22:01:08

the map can contain cljs symbols and forms which may be re-emitted (and wrapped) in dynamic cases but this code reodering could change meaning of the code, especially side-effecting code I think

darwin22:01:57

I have to emit original code in the strict order as it appeared in the map - top-down btw. this is issue in the current helix implementation as well

lilactown22:01:11

ah right. if you put a side-effecting function, it might re-order the effects

lilactown22:01:57

yeah. props tend to not contain a lot of side effects, I haven’t run into that corner yet but that could be horrible to debug

darwin22:01:15

this looks like a pretty hard nut to crack

darwin22:01:58

we could parse the raw source code behind map form but that sounds like a very heavyweight and error prone solution

lilactown22:01:07

tbh I’m a little bit surprised that iterating over it in CLJ doesn’t preserve the order

lilactown22:01:41

I thought smallish maps used an ArrayMap that had an implicit order, though woe betide those who might rely on that and then add one more key…

darwin22:01:48

it does if number of elements is <8, because then it is backed by array-map

lilactown22:01:10

ah, I didn’t realize it was so small. I thought it was like 32

darwin22:01:22

I discovered it by accident, writing some tests and then wondering that my generated code is not stable

darwin22:01:33

after touching small parts of it

lilactown22:01:43

I wonder how #js handles this

darwin22:01:18

that probably is the key, it is a reader tag which translates the map into (JSValue. …), I believe there is no map involved, and args order is used to gen underlying js object

p-himik22:01:42

Another potential reason to care about the order is the fact that iterating over JS object keys/entries is done in a specific order. Although, it's a bit more complicated than just preserving order. But I'm not sure if non-string keys are applicable in React, so maybe it is just the insertion order. An issue with parsing a map literal manually is that now you have to have a map literal - you cannot use any binding in its place.

lilactown22:01:42

:thinking_face: I thought the reader tags would still read the { … } literal into a map, just at read-time

darwin22:01:05

hmm, good point

darwin22:01:30

also I’m looking at JSValue just now and it seems to accept just one object as arg (which would be the map value I guess)

darwin22:01:45

so my previous comment was wrong

darwin22:01:36

@U2FRKM4TW I think our issue right now is codegeneration, we translate a piece of cljs into another piece of cljs code, but we don’t necessary preserve order of original map keys/vals - it is not related to runtime behaviour of created js objects

lilactown22:01:12

which just calls doseq on the map 😵

darwin22:01:35

how do you know that items is a map?

darwin22:01:24

hmm, this does not look correct (at first glance)

darwin22:01:25

I guess, nobody hit this issue because most js obj literals are small direct values without calling into side-effecting functions which would depend on each other

darwin22:01:43

and if they did they were probably not experienced enough to describe the issue and report it back

lilactown22:01:52

I can confirm this in self-hosted CLJS, at least

✔️ 4
darwin22:01:59

I can imagine having small-ish js obj literal with sideeffecting stuff and then adding one extra key would flip it from array-map to persistent map and my side effects get reordered

lilactown22:01:35

keys in #js literal do not preserve order when > 8 keys are present

darwin22:01:36

this is not a good proof, you would have to look at generated code, here you rely on runtime js iteration/presentation of js vals

p-himik22:01:44

I think the issue has nothing to do with React, props, etc. The issue is that you should not rely on the order of values in map literals. A similar topic has been discussed before - map destructuring where latter keys depend on the former. In general, it doesn't work. The solution, at least in that case, is to just compute everything that is order-sensitive before constructing the map. And then construct the map with the resulting values, already computed.

darwin22:01:49

or make an example with sideeffects

p-himik22:01:56

emit-js-object is the function that spews out the JS object. And it does iterate over a map created earlier via #js {...}. The order is not guaranteed.

darwin23:01:53

well, since the same issue is (probaly) in cljs itself, it could be our excuse to simply not care and leave it as undefined behaviour but I don’t like it, someone has working cljs code, wraps it in my macro and now suddenly it works incorrectly

lilactown23:01:56

@darwin I assumed the right hand side is the output source code

p-himik23:01:40

@darwin Just to make sure I understand correctly - it's your macro and not the user code that introduces the dependency on the order, right? I.e. the user might not care about the order at all.

lilactown23:01:58

@U2FRKM4TW the issue is that a user might write something like:

(props {:foo (i-do-side-effects)
        :baz (i-do-side-effects-too)})
and when that map gets > a certain size, it might re-order those side effects

lilactown23:01:02

which might be surprising to the user

p-himik23:01:18

@U4YGF4NGM It would be the error of the user.

p-himik23:01:26

The user must not rely on the order, that's it.

lilactown23:01:53

how do I tell users that lol

p-himik23:01:01

In that case it must be something like

(let [foo (...), baz (...)]
  (props {:foo foo, :baz baz}))

lilactown23:01:27

it’s surprising because if it was, in fact, a CLJS map the order of instantiation would be preserved

darwin23:01:37

I cannot gen such code, original order is lost when my macro is called with a map

p-himik23:01:48

@U4YGF4NGM How do you tell users to not make language mistakes in general? :) The limitations of the platform that affect everyone, including your library users, in general are not your library's concern.

p-himik23:01:58

@darwin But is it the user code that's order-dependent or the code within your macro? If the former, you don't need to do anything - otherwise, you would be just applying a band-aid on something inherently broken by the user. And there are many ways to break stuff, not just relying on map order.

lilactown23:01:57

@U2FRKM4TW I think the thing that is frustrating for darwin and I is that:

{:foo (i-do-side-effects)
 :baz (i-do-side-effects-too)}
is order-preserving

p-himik23:01:09

It. Is not. Guaranteed.

darwin23:01:59

well, then my macro is in line with this behaviour

p-himik23:01:21

The <= 8 thing is implementation detail. It will continue to work but you must not rely on it.

lilactown23:01:24

hmm. you’re right

darwin23:01:27

it just sucks, that I touch some small part of my map in tests and it generates reordered code

p-himik23:01:21

And the order of the execution will not be the same as the order of the computation of the items within the map literal!

p-himik23:01:56

I.e. in {:a (a), :b (b), ...many more items...}, (b) could easily end up being computed before (a) anyway.

darwin23:01:08

ok, thanks for the explanation, I have to think about this to accept this cruel fact of life have been living (and writing my code) under different assumptions 🙂

p-himik23:01:11

And it may continue to work for all eternity even for maps with thousands of items. But if the hash function is changed one day...

p-himik23:01:06

This just got linked to in #clojure: https://github.com/plumatic/plumbing#graph-the-functional-swiss-army-knife Seems like it could be helpful if you really want to avoid extracting every order-dependent thing into let.

p-himik23:01:27

(but it's a user thing, not something your library would do of course)

darwin23:01:40

looks like fnk is a macro and they can probably depend on macro expander behaviour to record order of fnk macros invocations - I have nothing like that, cannot force user to wrap his value in a macro call

4
darwin23:01:19

but I can probably live with that explanation you gave, it is simply something people must not depend on and I will work around my test issues by optionally converting incoming maps to sorted maps just for tests

darwin23:01:49

or develop better function for comparing codegen data

p-himik23:01:44

Seems like you're comparing JSON strings. But props can be functions - how would it work then? Or for any other type that's not JSON-friendly. I would probably just do a deep JS object comparison.

darwin23:01:27

indeed, this was just a quick way how to compare simple cases

darwin23:01:46

WIP, I will probably have to bring in some library doing deep object comparison

darwin23:01:06

suprisingly enough goog does not offer anything like that

darwin23:01:21

they have goog.object/equiv but that’s shallow

p-himik23:01:24

Actually, I retract my statement about deep comparison - you just need to compare each key-value pair and only delve deeper for the :style key.

p-himik23:01:05

Everything else must be = since you don't process the value in any way. It should end up as the very same object.

darwin23:01:17

deep comparison won’t hurt, style can be recursive (at least the code is currently written to work like that)

darwin23:01:25

> It should end up as the very same object. I’m not sure, did you consider dynamic cases as well?

p-himik23:01:40

OK, I can see that. But you still probably don't need a library for that since the only thing you would consider a parent is a plain JS object. There's a special case of NaN not being equal to itself but I'm not sure if there's anything else out there like that. > did you consider dynamic cases as well? What do you mean?

darwin23:01:44

maybe my last comment does not make sense, nevermind, I’m quite tired today

darwin23:01:05

btw. any ideas welcome, you can chime in in the GH issue,

👍 4
darwin23:01:14

thanks for all your help today, much appreaciated

p-himik23:01:52

Yeah, take my words with quite a few grains of salt as well - having e fever and sleeping 4 hours in the last 48 doesn't help. :)

😵 4
andy.fingerhut00:01:52

Please forgive me if I am beating a dead horse here, but here is a cljs Node-based REPL session demonstrating that the evaluation is not guaranteed to be source code order:

ClojureScript 1.10.597
cljs.user=> (def my-atom (atom 0))
#'cljs.user/my-atom
cljs.user=> {:a (swap! my-atom inc) :b "b" :c "c" :d "d" :e "e" :f "f" :g "g" :h "h" :i (swap! my-atom inc)}
{:e "e", :g "g", :c "c", :h "h", :"b", :d "d", :f "f", :i 1, :a 2}

8
lilactown22:01:35

keys in #js literal do not preserve order when > 8 keys are present