Fork me on GitHub
#clojurescript
<
2020-06-05
>
J02:06:18

What is a better way to "chain" / "pipeline" go channels together? e.g. an extremely dumbed down version using cljs-http (defn f1 [url] (cljs-http.client/get url)) (defn f2 [url] (cljs-http.client/post url)) What's the best way to chain/pipeline those functions together while adding some e.g. logging behaviours? basically I'm doing something like below, my code is very manual and redundant, so I thought there must be a more idiomatic / better way of doing the chaining/pipe lining. (log input) -> (fn1 input) -> (log output) -> (fn2 output) -> (use-output output) -> (log rssult) With go channel, it seems that I need to do something like this for almost every single function

(let [out (chan)]
  (go
    (let [result (<! (fn-returns-go))]
    (>! out result)))
out)

phronmophobic04:06:15

i’m not sure I understand the question. what about something like:

(defn my-process [input]
  (go
    (let [_ (log input)
          output (<! (fn1 input))
          _ (log output)
          output (<! (fn2 output))
          result (use-output output)
          _ (log result)]
      result)))

J04:06:52

thats pretty much what Im doing

J04:06:02

but if I have my-process-1 and my-procee-2

J04:06:22

above where the difference is how I log or how I use the output

J04:06:35

then the majoiry of above (my-process) code is duplicated multiple times

J04:06:51

I guess I'm trying to somehow be able to make my-process a partial function so that I can easily chain / re-group differnt part without having to do

(let [out (chan)] (go (blah)) out)
everywhere

phronmophobic04:06:56

go expressions return channels

phronmophobic04:06:25

so I would write the above as (go (blah))

phronmophobic04:06:03

(let [out (chan)]
  (go
    (let [result (<! (fn-returns-chan))]
    (>! out result)))
out)
would become
(fn-returns-chan)

J05:06:01

are you saying (go (blah)) would return the go channel from (blah)? I think I tested it, but wasn't getting the said behavior, I will test again. That's why I was doing (let [out (chan)] (go (>! out (blah)))) out) where (blah) returns a channel will, test and report back thanks very much

phronmophobic06:06:44

if blah returns a channel then you don’t need to do anything, you can just do (blah)

phronmophobic06:06:28

a go expression will return a channel that will receive whatever the value of the last expression is

👍 4
phronmophobic06:06:49

for example:

(go
  (<! (timeout 5000))
  42)
this go expression will return a channel that will receive the value 42 after 5 seconds

👍 4
phronmophobic06:06:52

it might be easier to illustrate if you have some sample code

J07:06:51

I got it!!!

J04:06:00

(defn f1 [a]
  (go
    (timeout 2000)
    (prn "after f1 timeout")
    a))

(defn f2 [a]
  (go
    (timeout 3000)
    (prn "after f2 timeout")
    a))

(defn f3 []
  (go
    (let [a (<! (f1 "hello"))
          b (<! (f2 "world"))
          c (str a " " b)]
      c)))

(prn (go (<! (f3))))

J04:06:12

Any reason the last prn doesn't work? Looks like the go block must be outside of prn like so (go (prn (<! (f3))))

phronmophobic05:06:39

the go expression returns a channel, so if prn is on the outside, it will just print out the channel itself. in clojure, you can do a blocking take with <!! if you want prn on the outside:

(prn (<!! (go (f3)))

phronmophobic05:06:05

there’s no blocking take in clojurescript

phronmophobic05:06:10

(timeout 2000) returns a channel, so it’s not going to wait unless you take from it:

(defn f1 [a]
  (go
    ;; park here until timeout chan times out
    (<! (timeout 2000))
    (prn "after f1 timeout")
    a))

phronmophobic05:06:25

an alternate way to write f3 that shows the flexibility of core/async:

(defn f1 [a]
  (go
    (prn "starting f1")
    (<! (timeout 2000))
    (prn "after f1 timeout")
    a))
(defn f2 [a]
  (go
    (prn "starting f2")
    (<! (timeout 3000))
    (prn "after f2 timeout")
    a))


(defn f4 []
  (go
    (let [achan (f1 "hello")
          bchan (f2 "world")
          a (<! achan)
          b (<! bchan)
          c (str a " " b)]
      c)))

phronmophobic05:06:24

the difference between f3 and f4 is that f4 will start running f1 and f2 at the same time instead of waiting for f1 to finish.

J06:06:57

thanks!

rickmoynihan07:06:41

Does clojurescript properly support data_readers.cljc? and evaling custom reader tags?

rickmoynihan07:06:41

e.g. I have

{g/uri my.namespace/->uri}
But evaling:
#g/uri ""
At a REPL gives:
Failed to read input: clojure.lang.ExceptionInfo: Attempting to call unbound fn: #'my.namespace/->uri {:type :reader-exception, :line 1, :column 19, :file "repl-input.cljs"}

thheller07:06:47

just call (my.namespace/->uri "") instead 😛

rickmoynihan07:06:14

That’s not why I want it… 🙂 I want to round trip printed values for otherwise unprintable objects back into my REPL.

rickmoynihan07:06:24

i.e. for REPL debugging large values

rickmoynihan07:06:42

without having to manually convert them all

thheller07:06:54

so the problem with reader literals in code is that in CLJS reading happens on the CLJ side

thheller07:06:56

and the way they are implemented requires the compiler to able to turn whatever was read into JS code which will then be evaluated by the client

thheller07:06:10

not sure how you get to round-trip anything that way properly to be honest 😛

rickmoynihan08:06:05

@thheller: ah of course! Makes total sense, though a little frustrating.

p-himik10:06:56

If your REPL is on CLJ and you really need to just send printed values from CLJS to CLJ, then you don't really need data readers on the CLJS side, right?

Eric Scott11:06:57

Why then does #inst work?

dnolen12:06:45

@rickmoynihan it should work, we have tests for the compile case

dnolen12:06:58

without more information, the issue is that you haven't loaded that namespace

dnolen12:06:38

note the error is a Clojure exception.

dnolen12:06:02

as pointed out - you need to handle both sides - the Clojure reading side and the runtime side - that can be done w/ .cljc of course

dnolen12:06:40

records are more or less free - but if your type is JavaScript stuff then you're going to have some challenges.

rickmoynihan12:06:58

@dnolen: Thanks… I was using a cljc file… but yeah I almost certainly haven’t loaded the ns as it’s in a shadow-cljs “compile process”… though I’m not sure how I can do that with shadow-cljs :thinking_face:

thheller12:06:22

shadow-cljs has no support for custom literals in code

rickmoynihan12:06:54

would you accept a feature request? 🙂

thheller12:06:17

for code in files no, for the REPL yes.

rickmoynihan12:06:42

just the REPL would be 👌

thheller13:06:19

supported in 2.10.2 if you add :compiler-options {:data-readers true} in your build config.

👍 4
jewiet13:06:55

Hello 🙂 I have an atom that is a set (the value is the check boxes of the columns to be displayed)

(reagent/atom #{:campaign-name
                                         :strategy-name
                                         :total-spend
                                         :average-spend-per-day})
Here I am adding a column if it is not present at the set and removing it if it is on the set.
toggle-column-selection (fn [column-name] (if (contains? @selected-columns column-name)
                                    (swap! selected-columns disj column-name)
                                    (swap! selected-columns conj column-name)))
Here is where selected-columns atom is dereferenced. This is where I need to put some order to display the column how i prefer them.
(case (:variant @strategies)
         :not-loaded [:div.alert.alert-light {:role :alert} "File not loaded"]
         :error [error @strategies]
         :correct [loaded-strategies {:strategies (:data @strategies)
                                      :selected-columns @selected-columns}])
Set offers membership check but also do not have guaranteed ordering. I want to custom order the columns displayed on the screen ( the screenshot below). How do I go about it? I want to reorder the columns like this: 1. campaign name 2. Age in days 3. uniques 4. post-click c. Thank you!

thheller13:06:15

@fana I usually keep multiple options in a separate collection, eg. (def my-options [{:value :campaign-name :label "Campaign"} ...]) which is in order. the set you can get from (set (map :value my-options)). this lets you couple a label and other additional things you may need with the keyword itself. whereever you are displaying the value just use my-options directly and have that check if :value is in the set.

jewiet13:06:23

@thheller Thank you, let me try it out.

Daniel Tan13:06:17

I use a vector of maps like [{:option-a {:checked true}} {:option-b {:checked false}] , serves me well, even if its a bit verbose

👍 4
naomarik15:06:09

I haven't looked at modern build tools in a long time. Does anyone have any resources or blog posts that compare the value proposition something like shadow-cljs (or others I haven't heard of) has over traditional lein/figwheel?

lilactown15:06:05

@naomarik this blog post isn’t a direct comparison but explains the motivation behind shadow-cljs: https://code.thheller.com/blog/shadow-cljs/2019/03/01/what-shadow-cljs-is-and-isnt.html

naomarik16:06:35

The main thing I like is the build report... only thing that's stopping me from building my project with shadow-cljs are my required foreign-libs.

bhauman16:06:25

@naomarik things are changing in figwheel.main for npm support because of underlying changes in clojurescript

bhauman16:06:46

but these changes are new

bhauman16:06:22

there’s a good explanation on that page of the mechanics

lilactown16:06:03

@naomarik shadow-cljs does support foreign libraries, it is slightly different than standard CLJS

lilactown16:06:16

the build report is really nice, I agree

naomarik16:06:32

@lilactown I saw several references that it was removed, also just spent a bit of time setting up a shadow-cljs.edn and that was the only thing stopping me from building my project with it. https://code.thheller.com/blog/shadow-cljs/2017/09/15/js-dependencies-going-forward.html

lilactown16:06:34

shadow doesn’t support `:foreign-libs` the CLJS feature, but it does support foreign code

naomarik16:06:36

I'm talking about the :foreign-libs key 😉 not foreign libraries in general.

lilactown16:06:09

when I converted our project at work to shadow-cljs, it was pretty simple to add a proxy namespace for the foreign libs we were using at the time

lilactown16:06:31

are you using CLJSJS or you rolled your own?

naomarik16:06:20

My current project is kind of a mishmash of buildtools right now but it's working for me, mainly using lein fighweel for development and the cljs build tool API directly (within boot) for advanced compilation. Have a few js files that I depend on that I'm able to consume with :foreign-libs option. Plan on cleaning it up later but it's not causing me any pain whatsoever right now, would really like the build report though.

lilactown16:06:18

yeah. one of the thing that isn’t made clear in that blog post you linked above is you can just require JS files in your CLJS namespaces

lilactown16:06:19

e.g.:

(ns my-app.feature
  (:require ["../js/some-lib.js" :as some-lib])
with externs inference on, we successfully migrated off of many of our internal foreign libs using that method and doing a simple mechanical translation to ESM in the JS files

lilactown16:06:41

it is work tho. best of luck either way 😄

naomarik16:06:17

Oh that helps! Will play around more later on when I have time.

chrismatheson18:06:20

hi there, i wonder if anyone might be abelt to help me figure out what im doing wrong wrt: shaddow cljs & emacs & repls etc

chrismatheson18:06:51

im using spacemacs and i can seem to get a REPL running with either :app or :test but not both ?

dpsutton18:06:53

there's nothing wrong with your project you're just asking about CIDER? if so come chat in #cider

chrismatheson18:06:17

mmmm actually i think ive got it now….

chrismatheson18:06:25

i can add a sibling session ??

chrismatheson18:06:44

if i do it directly

chrismatheson18:06:05

however if i try to start a session and then take the promopt to add a sibling instead then it seems to go weird

chrismatheson18:06:23

looks like im a few version behind latest shaddow-cljs as well, mgiht try and upgrade now before i get too far behind ! :d

chrismatheson18:06:58

newbie sort of question, but is watching the REPL the “normal” way to run tests?