This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-11-05
Channels
- # bangalore-clj (5)
- # beginners (77)
- # boot (29)
- # cider (11)
- # clojure (110)
- # clojure-dev (5)
- # clojure-greece (1)
- # clojure-poland (1)
- # clojure-russia (11)
- # clojure-spec (1)
- # clojurescript (143)
- # clojurex (1)
- # core-async (1)
- # css (1)
- # cursive (40)
- # data-science (1)
- # datomic (6)
- # figwheel (4)
- # fulcro (45)
- # immutant (5)
- # off-topic (4)
- # onyx (8)
- # portkey (9)
- # re-frame (112)
- # shadow-cljs (30)
- # spacemacs (3)
I'd like to swap out a few functions between a "development" build and a "production" build of a cljs program. Think using (foo-with-instrumentation x y z)
in dev and (foo x y z)
in prod. The only way I can think of doing this is to use goog-define
vars:
(def foo (if DEV? dev-impl/foo-with-instumentation impl/foo))
where the value corresponding to DEV?
can be changed w/ the :closure-defines
cljs compiler option.
However, since the instrumented functions live in a different namespace than the uninstrumented one, does this mean I need to pull in those namespaces with toplevel require
rather than in the ns
form?
Not sure if this is the best way to go about things, or if there's a neater solution.
@kevin.lynagh top level require
is basically the same as ns
. it can’t be conditional either. you add a :preload
in dev that just uses set!
to change the impl of foo
.
Got it, thanks for the reply. I'm nervous about the idea of using set!
lest I forget to override every var. I'm going to try having identical namespaces and choosing between them via source-paths.
@kevin.lynagh i wouldn't call it neat but you could have two directories which each define the same namespace, dev/my/foo.cljs
and prod/my/foo.cljs
, then set the source paths in the dev/prod profiles
Yeah, this sounds like it might be the easiest way forward. I've done this hack in the past. Thanks!
I'm having a weird bug, and I have no idea how to track it down. I have a SPA where I use hash urls (/index.html#/foo), and some radio inputs in a form. well for some reason, when executing an event (which only updates an atom) my whole page gets reloaded as /index.html?bar=baz#/foo, where bar is the name of the radio group, and baz the value of chosen radio input. Any idea how to track down the code that is reloading my page? (breakpoints on global events like onbeforeunload will not show me a relevant stack trace)
@roti, the form gets submitted?
its a simple onlick event with a function that just swaps an atom (after which the dom gets rerendered)
try adding onsubmit="return false"
to your form, just for shits and giggles
@pesterhazy now it works fine 🙂 great idea, thanks
beats me!
🤕 I learned something new today: https://stackoverflow.com/questions/2825856/html-button-to-not-submit-form
@pesterhazy thanks again
How do I pass a clojurescript function to a JS function that expects a callback? Do I have to convert it somehow or is it toll-free bridged?
toll-free!
try planck -v
and enter (defn foo [n] (* n n))
- you'll see what it compiles down to
For an even simpler (Var-free) view of things see what you get without the def
. Evaluate (fn [n] (* n n))
👍 for planck -v
- very useful to explain stuff to beginners with solid JS knowledge
thanks for adding this!
is there a collection function (or combination of) with which I can search in a vector (using a predicate) and get the index? but without going through the whole array. I need something like some
which returns the index
AFAIK, there is no core fn for this. Andre is right, and there are others at https://stackoverflow.com/questions/8087115/clojure-index-of-a-value-in-a-list-or-other-collection
@roti The use of reduced
should prevent traversal of the whole collection. A concrete example
(reduce-kv
(fn [_ k v]
(prn k)
(when (= v :c)
(reduced k)))
nil
[:a :b :c :d :e])
^ Edited to show steps via prn
@ajs Yeah, I suppose the reduce-kv
variant might be faster if you are dealing with vectors, while your example is generic for sequences.
I think you can get a backtick by holding down the tick key.
reduce
is explicit iteration which I try to avoid as much as possible if there are other functions available
i know it's alpha but I wanted to play around with it a bit I can't get the basic example working from the blog post, i don't know what i'm doing differently
@ajs @roti I was curious about the perf of reduce-kv
on vectors vs. generic seq fns. Here is a result: https://gist.github.com/mfikes/eee355193cb6b8a3c1124c8514da8432
@ajs If you set up the ClojureScript compiler for running tests (see https://clojurescript.org/community/running-tests), then you can run ./script/benchmark
. With that, it is an easy matter to modify this file https://github.com/clojure/clojurescript/blob/master/benchmark/cljs/benchmark_runner.cljs to include new tests that you might be interested in, and it will run them across all of the JavaScript VMs you've set up. You can also delete everything from line 11 down, and replace it with just the benchmarks you want to run.
One bit of advice is to watch out for benchmarking expressions that Closure can completely optimize away. (Sometimes you need to "let" a fn containing the expression of interest in the simple-benchmark
binding portion and then call it in the body.
I just discovered simple-benchmark
in cljs, so for less aggressive benchmarking, that will probably help me test little functions like this in the future
@mfikes @rauh thanks, I was planning to join the values with their indexes and then use some
, but I think reduce-kv is better
Yeah, it is all just tradeoffs. If you concretely know you are operating on vectors, you can play games like this. 🙂
does clojurescript have any mechanisms similar to user.clj
that would let me monkeypatch things before the primary code runs?
Thanks! I thought there was some weird thing going on but in the end the clojure syntax was obscuring (to my untrained eye) my misuse of the JS API :)
I use try/catch in a go block and it doesn't seem to catch. am I doing something wrong?
(defn testex []
(go
(try
(let [response (<! (http/get "" {:with-credentials? false}))]
(println response))
(catch :default e
(println "error" e)))))
@roti any channel op breaks out of the enclosing try
core.async is turning what looks like sequential imperative code into a bunch of callbacks - the exception is inside your callback
@roti it is, but your <! contains an http/get that is not inside your try 😄
it's actually a callback, for readability sake core.async makes it look like it is
if that's where you expect the exception, yes
or, another way to put it is that if http/get is the thing throwing, it should have a try around it
I think... but http/get is giving you a channel which makes me think things might be trickier than that...
I would need to read the code to help more at this point I think
maybe this can help: http://martintrojer.github.io/clojure/2014/03/09/working-with-coreasync-exceptions-in-go-blocks
@pontus.colliander that's very helpful advice, and if the authors of the http lib roti was using had followed it, roti would not have this problem
@noisesmith nope, doesn't work. here's the code:
(defn testex []
(go
(let [response (<! (try (http/get "" {:with-credentials? false})
(catch :default e
(println "error" e))))]
(println response))))
the answer here might be using a different lib, or a PR to fix the one you are using
so, http/get
returns a channel, which apparently is not happening when an error occured (in my case the response is of type json, but the content is not so http can't parse it, so it throws an error)
which http lib is it?
actually I'm not so sure where the error is thrown, because if the channel is not returned because of an exception, it should be caught.
Uncaught SyntaxError: Unexpected token S in JSON at position 0 at JSON.parse (<anonymous>) at cljs_http$util$json_decode (util.cljs?rel=1509894674732:63) at core.cljs:4829 at Function.cljs.core.update_in.cljs$core$IFn$_invoke$arity$3 (core.cljs:4829) at cljs$core$update_in (core.cljs:4820) at cljs_http$client$decode_body (client.cljs?rel=1509894676467:83) at Function.<anonymous> (client.cljs?rel=1509894676467:181) at Function.cljs.core.apply.cljs$core$IFn$_invoke$arity$2 (core.cljs:3685) at cljs$core$apply (core.cljs:3676) at async.cljs?rel=1509880110795:712 cljs_http$util$json_decode @ util.cljs?rel=1509894674732:63 (anonymous) @ core.cljs:4829 cljs.core.update_in.cljs$core$IFn$_invoke$arity$3 @ core.cljs:4829 cljs$core$update_in @ core.cljs:4820 cljs_http$client$decode_body @ client.cljs?rel=1509894676467:83 (anonymous) @ client.cljs?rel=1509894676467:181 cljs.core.apply.cljs$core$IFn$_invoke$arity$2 @ core.cljs:3685 cljs$core$apply @ core.cljs:3676 (anonymous) @ async.cljs?rel=1509880110795:712 (anonymous) @ async.cljs?rel=1509880110795:702 cljs$core$async$state_machine__29062__auto____1 @ async.cljs?rel=1509880110795:702 cljs$core$async$state_machine__29062__auto__ @ async.cljs?rel=1509880110795:702 cljs$core$async$impl$ioc_helpers$run_state_machine @ ioc_helpers.cljs?rel=1509880107319:35 cljs$core$async$impl$ioc_helpers$run_state_machine_wrapped @ ioc_helpers.cljs?rel=1509880107319:39 (anonymous) @ ioc_helpers.cljs?rel=1509880107319:48 (anonymous) @ channels.cljs?rel=1509880106192:61 cljs$core$async$impl$dispatch$process_messages @ dispatch.cljs?rel=1509880105746:19 channel.port1.onmessage @ nexttick.js:211
> actually I'm not so sure where the error is thrown, because if the channel is not returned because of an exception, it should be caught. if the error is thrown inside someone else's go block, your try/catch cannot catch it
and if core.async runs the code that throws the error before the code that uses the channel, you never see the evidence the channel got returned
IMO there's little reason to use a library for HTTP requests in CLJS. Just use js/fetch
I'd also recommend against introducing core.async unless you have a serious need for its features
@pesterhazy RE core.async
> I'd also recommend against introducing core.async unless you have a serious need for its features
Would this include using it to create functionality like that produced by setInterval
?
@tkjone, I can’t imagine creating an clojurescript application in the browser without core.async. I think it’s an easier way to deal with the event handling and callbacks that are apart of most js apis
including creating functionality similar to setInterval
(probably using timeout
from core async)
That was also my impression, but I wanted to see under which cases core.async is "too much"
@tkjone yes of course, why would using setInterval necessitate the introduction of macros and channels?
that’s just the way i’m most comfortable. I’m not saying it’s the right thing everyone
I’ve already gone through learning some of the quirks of core async
so I just start with core async at the beginning
by the way, often using https://google.github.io/closure-library/api/goog.Timer.html would be a good alternative to using setTimeout directly
if you’re working on a project with a team, there is somewhat of a barrier to entry with picking up core async, so using goog.Timer or setTimeout directly is definitely more straightforward
the macrology involved with core async can also make debugging more difficult.
I'd argue core.async has a high cost even in a team that knows it well
yea, i prefer using core async, but I totally get that many would avoid core async unless it’s doing a lot of heavy lifting
This is one of those interesting things about learning CLJ/S: There are a lot of mechanisms that are capable of doing the same things, but not all of them should be used in all scenarios. I think this might present itself a little more in CLJS because, it feels to me, that the interop is used more often in CLJS.
Earlier today I was prompted to write something like this:
(go
(loop []
(async/<! (async/timeout 16))
(do-something)
Because I figured it was more idiomatic than the simple js/setInterval
(had no idea about goog/timer)
This is not to say that other languages do not have this issuecore.async has pros and cons, but i wouldn't use it just because it's idiomatic (I'd argue it isn't, not on the server and especially not in the browser)
This is interesting because I feel that I have read quite a bit about the language and still find it challenging to find out what the right tool for the job is. My example was idiomatic setInterval
, but this also applies to other areas like ..
v. ->
macro etc
I agree, a lot of this is tribal knowledge
..
being deprecated-ish in favor of ->
from the outside it may look like everyone in clojure(script)land is using core.aysnc, but that's far from true
See, I did not know this. ..
is often passed on as an okay practice, which I am sure it is, but as you mention ->
is likely better practice.
And yes, core.async does seem to be widely used
The question is, how can the tribal knowledge be accessed?
here 🙂
people blog, or give talks
but I agree that this knowledge, or experience, is not easily accessible
would you say other communities do a better job at this?
I find that it's worse in JavaScript, because some of the material you find online is written by novice programmers, so you need to watch out for signs that the author doesn't really know what they're talking about
haha True and I accept that answer because it is true. With this in mind, if all developers are working on different teams, the tribes split and evolve and grow. How do we keep track of the evolution? Yes, I believe that the Python, Go and JS communities do an excellent job of this this. Emphasis on JS. JS has other issues, but it is very easy to find what all the tools are for one task, and find write ups on when to use them.
I also agree about writeups from novices
I have been steered in the wrong direction several times because of this. This is both in JS and Clojure, but with JS you know the 10 most reputable developers and you can use their work to vet others writings
In Clojure, I know the reputable developers, but their writings, valuable as they are, are sparse
agree with that
but you need to understand that python, js etc. are orders of magnitude larger than we are
Definitely true. This is understood.
I'd like to blog more, but it's hard work and you don't immediately see the effects
I hear you. What would you say is a good approach to asking the clojure community to support efforts that attempt to share the tribal knowledge. Essentially asking people to peer review your writings? I generally just ask whichever channel I believe would be able to contribute, but there is a line and I want to be sure its not being abused
I know we have the community driven API docs, but I find that those make assumptions about learning styles and also context is not given so understand things like when to use one tool over another
personally I'm always happy for people to ask for feedback on the writing
I think most clojurians feels the same
Yes, I have only had positive responses thus far. Whatever I have said about CLJ/S at large, this forum is likely one of the best I have ever experienced. So CLJ/S is a friendly tribe
Here is a fun one. I am working with an experimental browser feature: webkitSpeechRecognition
. When you speak, you are going to get back a SpeechRecognitionResultList
which is distinct because it is not an Array.
Here is some sample code:
(set! js/SpeechRecognition (.-webkitSpeechRecognition js/window))
(def recognition (js/SpeechRecognition.))
(set! (.-interimResults recognition) true)
(defn handle-speech
[results]
(map (fn [results] (get results 0)) clj-results)
(.addEventListener recognition "result" handle-speech)
This will error out when you try to speak into your speaker with Uncaught Error: [object SpeechRecognitionResultList] is not ISeqable
. I ran into something similar with nodeList
and I was able to resolve it with a protocol like:
(extend-type js/NodeList
ISeqable
(-seq [node-list] (array-seq node-list)))
However, this won't work for the above, because there is no JS/SpeechRecognitionResultList
object.I don't understand the snippet. Where's e
used?
Anyway can't you just use array-seq
directly?
also it looks like you're using map
for side effects. Don't do that. Use doseq
or run!
Sorry, e
was a mistake. It should be results
(get results 0)
would be a side effect?
no but... handle-speech is an event listener, so its return value is presumably ignored?
Oh, yes, 100%. Sorry, I will be storing the value returned by Map and doing some more processing in the future. But right now, I was just running some tests and wanted to illustrate the problem. Good point though!
anyway try (->> results array-seq (map ...) prn)
Sounds good.
Hello, a question about advanced optimization:
I am using D3.js in CLJS, and have a D3 callback function (the anonymous function below) that receives a previously bounded data as input from D3. Without advanced optimization, I get the expected numeric value in minutes
, but with optimization, I get undefined
. Any ideas?
(.attr "transform"
(fn [d i]
(let [minutes (.-minutes d)
y (y-scale minutes)]
(str "translate(0, " y ")")))))
I am completely new here, but I have some experience with the Closure Compiler. Perhaps the optimizations are renaming the minutes property during minification?
ah. is there a way I can avoid the renaming? Also, wondering if I can avoid writing problematic code like this
hmm, doesn't look like it AFAICT. just logging with _ (js/console.log (.-minutes d))
gives me undefined
In JS world, using brackets to access a value eliminates it as a candidate for renaming. So d['minutes'] would be safe.
I think in that case it is still possibly minifying the '.-minutes' part
I would inspect the js output maybe to see if that's the case
In terms of a Clojure-y way to represent bracket syntax, maybe using (:minutes d) would work? I'm a newb here coming from JS land, so this is just a guess.
Hmm, that doesn't seem to work. Maybe using aget? But that might just be equivalent to .-
Looks like this page outlines a solution you can use. Simply put: (aget obj "propertyName")
@U7W5MHG1L that does work, thanks much!