Fork me on GitHub
#clojurescript
<
2018-07-10
>
jsa-aerial00:07:18

Is :require-macros no longer the 'correct' way to get macros, but rather in a require for a name space add :refer-macros [....] to the require??

dnolen00:07:21

@jsa-aerial everything desugars to :require-macros if you want to use it, use it - but there’s little need nowadays

jsa-aerial00:07:24

OK - I just was having some issues (probably cockpit errors...) trying to get it to work with Specter, while the :refer-macros just worked

mfikes01:07:56

@akond patch for https://dev.clojure.org/jira/browse/CLJS-2812 has landed on master This effectively lets you extend IPrintWithWriter to native types

caleb.macdonaldblack01:07:58

What are peoples thoughts on core.async vs promises? I feel that I like core.async but as soon as you need to handle errors/exceptions it becomes difficult to manage. Promises seem to deal with exceptions in a much nicer way than core.async.

dnolen01:07:29

I don’t really follow the “difficult” to manage bit, since you have be very careful with promises to in real programs when it comes to exception handling

caleb.macdonaldblack02:07:10

Channels would require us to catch the exception in the thread, pass it into the channel and then another check when taking from the channel to see if there was an error. In comparison a promise will need a single check in the thread to catch and invoke the reject callback and expect errors in the .catch. The .catch acts like out second check. Plus they bubble up which is handy. I’ve only used core.async a couple times on some simple stuff so there may be a better way to deal with errors

dnolen02:07:55

I would say this analysis is not really accurate at least in my own experience wrt. core.async or promises

dnolen02:07:14

both approaches require you to reason about responsibility

dnolen02:07:27

with promises you cannot randomly catch stuff

dnolen02:07:41

that’s the same as randomly catching exceptions in normal control flow

dnolen03:07:07

once you catch there is no bubbling up

dnolen03:07:13

as far as channels go, yes you have to write a couple of more lines - something like <!? which throws on errors

dnolen03:07:21

then the result is more or less the same

dnolen03:07:41

you get a exception in the console, just like a unhandled promise rejection

dnolen03:07:41

in practice I would say this isn’t a big deal - if you have some long async chain or some long promise chain - you have bigger design problems in your program

au-phiware03:07:57

can someone help me interpret this v1.10.339 compiler error, please? I'm getting java.io.NotSerializableException: cljs.tagged_literals.JSValue https://paste.ee/p/f1fY3

au-phiware03:07:12

(my project compiles under v1.10.238 but not 339... I'll try another compiler release...)

au-phiware04:07:02

I did a git bisect and the first bad commit is https://github.com/clojure/clojurescript/commit/0cdbb23abeeabb36c1ce0a6010890aa97da0fad1 ...but it still makes no sense to me

au-phiware04:07:10

why is the JSValue not serializable and what is the JSValue?

au-phiware04:07:14

I realise that the exception is coming out of adzerk.boot-cljs.util/serialize-object... should I take it to the boot channel?

au-phiware04:07:42

hmmm, I think this may be related: https://github.com/boot-clj/boot-cljs/pull/181 ...but I'm not using spec...

au-phiware05:07:46

using flyboarder's patch on boot-cljs seems reveal this error "failed compiling constant: cljs.tagged_literals.JSValue@498ba0c9; class cljs.tagged_literals.JSValue is not a valid ClojureScript constant. failed compiling file:/home/corin/.boot/cache/tmp/home/corin/src/b2r/-ghcvds/reagent/debug.cljs" ...I'm still clueless...

caleb.macdonaldblack07:07:31

@dnolen thanks for the advice. Ill continue to play around with core.async and see how I go.

pesterhazy07:07:59

@caleb.macdonaldblack I'm on the Promises side: - no library need: works out of the box from figwheel/node repl/lumo - only does one thing — reifying async computations — and does it well (core.async also handles async rewriting) - tiny API surface (only constructor, .then, catch) - easy interop with platforms APIs like fetch, react-native, ... - Promise.all covers 80% of slightly-more-complicated concurrency needs, at least in the browser - hits a sweet spot for error handling with .catch - (-> promise (.then (fn []))) notation makes it easy to follow the data flow - macros optional. You can use async/await-like alet macro form the Promesa library (but you may not need it in many caes)

👍 4
pesterhazy07:07:49

By contrast, core.async strikes me as a power tool for complex async logic and comes with additional cognitive overhead (and usually you're better avoiding async complexity as much as possible). It's also an amazing technical achievement but that doesn't mean it's right for most browser-based apps.

leonoel07:07:57

@pesterhazy you don't need to reify an async computation to make it composable

leonoel07:07:34

all the features you listed are achievable without reifying computation

pesterhazy07:07:49

@leonoel maybe? I didn't argue that promises are the only abstraction that makes async computations composable

leonoel08:07:53

you didn't, it was just a reminder that asynchronous programming is more complex than "promises or core.async, choose one" 🙂

pesterhazy08:07:45

@leonoel "choose one of these two" is a pretty good practical description of your practical situation in CLJS today

leonoel08:07:49

because of interop ?

leonoel08:07:14

promises support callbacks, with callbacks you can interop with anything you want, I don't really see why we should be in such a status quo

pesterhazy08:07:12

all I'm saying is that if you're dissatisfied with straight callbacks it makes sense to pick an abstraction on top of callbacks, and in that space the salient alternatives are core.async and promises

leonoel08:07:16

and users seem to have objections to both

leonoel08:07:40

and this is a recurring problem, a fair amount of traffic on this channel is related to this

pesterhazy08:07:28

sorry, I don't understand your point

leonoel08:07:17

my point is just that the two most popular solutions have pros and cons, so both sides should listen to each other and maybe look at alternatives

pesterhazy08:07:24

I think we're on the same page then 🙂

akond10:07:39

@mfikes supercool, thank you

felbit11:07:10

Hello fellow Clojurians, I try to get js routing with secretary working, but it just won’t do. Is someone available who may be able to help out? Don’t want to flood the channel with code samples.

hlolli11:07:08

I've been useing react-router lately with great success, throwing ideas if secretary fails. I used secretary in the past, so I guess you'd need to ask a specific question, post pastebin link if it's huge block maybe?

felbit11:07:35

@hlolli Thank you for the response. I’ve created a gist here: https://gist.github.com/MarPenc/bb2311bf534f68c9f785bf5acc3aaff7 — it is from the picture_gallery example out of the book @yogthos has written on web development in Clojure I am through the whole thing, just cannot get this piece to actually react to anything.

hlolli11:07:11

Yes, I guess it's a case of react not recognizing the change. The components react to props change or local state change, and I can't see that being changed in your example.

hlolli11:07:52

with re-frame it would be easy, but with re-agent, I'd pass in the state or cursor to the router state as parameter, and mutate some ratom on router change.

felbit11:07:18

@hlolli Thank you. Guess, I’ll figure out, how to do that.

hlolli11:07:11

secratary is good, but this was exacly the problem I had with it, as well as bidy, it's not a react component, so you need to find a way for these two worlds to react to each other.

hlolli11:07:25

so yeh, just make the component which are in render, read from a state (ratom), that is being changed by secretary and I think you'll be fine

felbit14:07:23

@hlolli I have to follow up on this, because it turned out, I don’t get it. What do you mean by an ratom that is changed by secretary? I know ratom and I get how to change it. But how do I get secretary to change it?

hlolli14:07:35

in your defroutes, do you swap! any ratoms. Are your defroutes body being called (check with println) when the routing changes? Maybe it's only on initial load, then it would mean the goog.history isn't sending events.

hlolli14:07:10

ah ok, I see also that you're using re-agent session there

felbit14:07:17

yes, I do … should I not?

felbit14:07:50

It holds the auth information

hlolli14:07:56

yup, that's actually best practice. I've been useing re-agent longer than the session namespace, so I have different practice 🙂 but let's break it down, does (session/get :page) change?

hlolli14:07:42

you could

(defn page []
  [:div
   [modal]
   [:h1 (str (session/get :page)) ]
   [pages (session/get :page)]])
often I do something like this to help me see if it's changeing or not.

felbit14:07:43

ok, let me see. I tried that in the repl, but not in the browser

felbit14:07:14

the h1 string actually doesn’t show up at all

felbit14:07:37

[:h1 (str "Session: " (session/get :page))]results in Session: - so I guess there’s no session here.

hlolli14:07:16

which means that neither defroutes got called

hlolli14:07:06

;; Quick and dirty history configuration.
(let [h (History.)]
  (goog.events/listen h EventType/NAVIGATE #(secretary/dispatch! (.-token %)))
  (doto h (.setEnabled true)))
add this to the bottom of your page?

hlolli14:07:52

from the example, you would need maybe some different require's imports

(ns example
  (:require [secretary.core :as secretary :refer-macros [defroute]]
            [goog.events :as events]
            [goog.history.EventType :as EventType])
  (:import goog.History))
just add what gets complained about

felbit14:07:53

works like a charm

felbit14:07:38

Thank you, that actually works!

felbit14:07:44

I think, there is something missing in the book. At some point it states that the init! function shoult look like this:

(defn init! []
  (load-interceptors!)
  (hook-browser-navigation!)
  (mount-components))
But the function (hook-browser-navigation!)is nowhere to be found so I have it commented out. Maybe that is exactly the missing part.

hlolli14:07:33

maybe these interceptors are supposed to add the event listener to html5.

hlolli14:07:40

but great, all there, cheers

felbit14:07:59

Thanks again!

felbit11:07:07

ah! I think, I get it. I’ll try that.

mfikes13:07:22

@au-phiware When the compiler reads a JavaScript array or object literal, the result is a JSValue. Normally this passes through to the compilation stage where the associated JavaScript literal is emitted in the JavaScript being output. But, if you have code like the following example

(let [#js [1] 3])
then the compiler would throw an “Unsupported binding form” exception containing the JSValue object, and normally this would be displayed as
clojure.lang.ExceptionInfo: Unsupported binding form: cljs.tagged_literals.JSValue@216fea48
...
but if an exception like this needs to be serialized, that’s where I think you encounter the issue.

caleb.macdonaldblack13:07:06

@pesterhazy thanks for your input on promises. I think Im leaning more towards promises for most async problems in cljs. I can see core.async being useful in more complex async problems too. As for my current problem I think promises are the more appropriate solution. Thanks again

👍 4
dnolen14:07:14

@lilactown re: preventing dead-code elimination - what are you actually trying to do? Then maybe something can be suggested.

dnolen14:07:30

if you’re just trying to make sure some ns survives so JS code can call it - there is an officially supported way - exporting via goog.exportSymbol

👍 4
lilactown15:07:49

@dnolen I was trying to create a release build of my devcards to deploy and share with non-technical members of my team. the way I have it setup is there's a top-level ns that just require's all of my devcards ns' and calls dc/start-devcards-ui!

lilactown15:07:54

because all the devcards ns' just have a bunch of defcard with no explicit side effect / not being called from other ns, they were being eliminated. my solution was just to add a (def ^:export keep! "This makes it so that the release build doesn't remove this NS") ¯\(ツ)

dnolen15:07:49

@lilactown given this is just for fun then why do you need advanced? simple will suffice no?

lilactown15:07:07

AFAICT it was happening with simple too? now I'm questioning myself

thheller16:07:35

@lilactown are you sure its not devcards being compiled out? it has some stuff to ensure they are only available in development usually

lilactown16:07:56

yep! the first time I did the release build, the devcards UI came up but only 3 namespaces showed in the UI 😂

lilactown16:07:20

turns out they each had a println in them that was preventing them from being eliminated

dnolen16:07:35

@lilactown DCE doesn’t apply to :simple

thheller16:07:20

a single println doesn't keep anything else from that file alive though so there must be something else going on.

lilactown16:07:20

just to double check myself, I went back and deleted or commented out the ^:exported variables and it made no change - the namespaces still show up in devcards o_O

lilactown16:07:27

this is stranger than I thought

thheller16:07:19

did you try :simple? maybe devcards just don't like :advanced? maybe @bhauman can clarify if they are supposed to work in :advanced builds

lilactown16:07:44

tried simple before in my initial testing and saw the same behavior, but currently using :advanced

john16:07:51

I don't know much about devcards, but I'd think if the top-level ns you mentioned is 'requiring all your devcard ns's' then they shouldn't get DCEd anyway.

john16:07:14

hmm, looks like defcard* doesn't actually def anything in the current namespace actually

andrewzhurov18:07:41

trying to :refer :all

(ns some-ns
  (:require [another-ns :refer :all]))
And getting "Keyword is not ISeq" is it not supported or I've made a mistake somewhere?

andrewzhurov18:07:49

we forgot to mention there :refer :all case though thanks:)

dnolen18:07:13

@brownmoose3q ah yeah, feel free to open up issue on the clojurescript-site - https://github.com/clojure/clojurescript-site

au-phiware23:07:53

@mfikes, thanks for the background. What about the "failed compiling constant: cljs.tagged_literals.JSValue; class cljs.tagged_literals.JSValue is not a valid ClojureScript constant." that I encountered? Are you familiar with that particular message? In what sort of situation would that arise? (Sorry, I didn't capture the full stack trace of that one... I've had to revert to a working version and move on to more pressing matters)

mfikes23:07:25

@au-phiware When a JSValue makes it through to the compiler and is going to be emitted, it is supposed to be dispatched here https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/compiler.cljc#L393 but something odd in your case (perhaps multiple classloaders?) seems to have caused it to end up in the :default branch here https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/compiler.cljc#L268

mfikes23:07:24

As an aside, we encountered exactly that sort of problem with self-hosted when working on a patch in master recently, and the root cause was an accidental double-loading of the cljs.tagged-literals namespace, but that would be unrelated to what you are doing with JVM-based ClojureScript / boot, etc.