core-async

2020-05-22T15:17:26.281Z

One thing I’ve always struggled with is how to model clojurescript UI DOM components (ie an arbitrary DOM tree) using core async. And I tend to come back to attempting this every few months :) How do I expose from a UI component the need to trigger dom events on internal elements, and the output of events triggered on internal elements? It seems like the options are Have a general input and output chan for each ui component. Could either wrap the events and triggers in a way that meets my needs (IE [::focus] could trigger the dom .focus event on an internal element. And something like [::query-input “query string”] could be put on the output chan. One problem with this is the the events sometimes in theory represent separate async processes (ie a mouseover event could occur simultaneously as a click event), but it’s more rare for this to be relevant. Have a specific input/output chan for each needed event type. Ie have the component have a :query-input-chan which outputs changes to the internal query input element. Expose as many chans that are needed for each component. I’m working on an emacs-like editor thing where there are windows and buffers in a windowing system where the buffers can be basically anything (ie a text editor, file picker, repl, etc). Each buffer is sort of it’s own entities and has its own lifecycle, dependencies and sub-processes. I haven’t found this to fit will with the reframe global message bus style, but I haven’t been satisfied with my attempts at either of the above approaches in isolated components either. Any general tips/tricks or thoughts about this type of thing?

ghaskins 2020-05-22T18:13:54.281900Z

hi all, im struggling trying to understand how to apply a stateful transducer to a core.async channel

ghaskins 2020-05-22T18:14:06.282200Z

specifically, im interested in using this lib: https://github.com/MastodonC/kixi.stats

ghaskins 2020-05-22T18:14:33.282900Z

i have a stream of data with messages like {:duration 34.56}

ghaskins 2020-05-22T18:14:51.283300Z

and I want to execute a summary on them, getting the result once the stream closes

ghaskins 2020-05-22T18:15:18.283500Z

any guidance appreciated

2020-05-22T18:22:17.283900Z

you can't

2020-05-22T18:23:04.284500Z

transducers on channels do run a completion step, but the result of the completion step is ignored

2020-05-22T18:23:30.284800Z

(the channel is closed nowhere to put it)

2020-05-22T18:24:33.285400Z

you may want something like async/transduce (a transducer over a channel) instead of a transducer passed to a channel

2020-05-22T18:25:09.285900Z

(a transducer in a channel?)

ghaskins 2020-05-22T18:25:57.286900Z

that makes some sense

ghaskins 2020-05-22T18:26:35.287300Z

actually now that I look at async/trasnduce, I think I get what you are saying

ghaskins 2020-05-22T18:26:37.287500Z

let me try

ghaskins 2020-05-22T18:26:42.287700Z

(ty)

ghaskins 2020-05-22T18:31:12.288400Z

@hiredman My first attempt looks like this

(async/transduce :duration kixi/summary ch)

ghaskins 2020-05-22T18:31:22.288700Z

this of course doesnt work since its an arity-4 function

ghaskins 2020-05-22T18:31:28.288900Z

but im not clear what I need for init

2020-05-22T18:33:04.289800Z

it looks like kixi/summary might be a reducing function, not a transducer

2020-05-22T18:34:45.290500Z

e.g. you would apply it like (reduce summary collection-of-numbers)

2020-05-22T18:35:22.291Z

async/transduce takes four arguments, xform, f, init, and ch

2020-05-22T18:35:33.291300Z

xform is a transducer

2020-05-22T18:35:37.291500Z

f is a reducing function

2020-05-22T18:35:53.291900Z

init is the initial value for the accumulator when reducing

2020-05-22T18:35:59.292100Z

ch is the source of data

ghaskins 2020-05-22T18:36:48.292400Z

right...if I run it like this

ghaskins 2020-05-22T18:36:50.292700Z

(transduce identity kixi.stats.core/summary [1 2 3])
=> {:min 1.0, :q1 1.25, :median 2.0, :q3 2.75, :max 3.0, :iqr 1.5}

ghaskins 2020-05-22T18:36:52.292900Z

it makes sense to me

ghaskins 2020-05-22T18:37:02.293400Z

im not sure how to map that to async/transduce

2020-05-22T18:38:07.294600Z

1:1 you transduce just requires an initial value, which you might be able to get by calling summary with 0 arguments

ghaskins 2020-05-22T18:38:56.295200Z

i guess this is closer to my use case

ghaskins 2020-05-22T18:38:57.295500Z

(transduce (map :duration) kixi.stats.core/summary [{:duration 1.0} {:duration 2.0} {:duration 3.0}])
=> {:min 1.0, :q1 1.25, :median 2.0, :q3 2.75, :max 3.0, :iqr 1.5}

2020-05-22T18:39:40.295900Z

if you look at the docstring for transduce:

2020-05-22T18:39:45.296200Z

clojure.core/transduce
([xform f coll] [xform f init coll])
  reduce with a transformation of f (xf). If init is not
  supplied, (f) will be called to produce it. f should be a reducing
  step function that accepts both 1 and 2 arguments, if it accepts
  only 2 you can add the arity-1 with 'completing'. Returns the result
  of applying (the transformed) xf to init and the first item in coll,
  then applying xf to that result and the 2nd item, etc. If coll
  contains no items, returns init and f is not called. Note that
  certain transforms may inject or skip items.

2020-05-22T18:40:10.296800Z

it can also take an init, and if an init is not provided it calls f with no arguments to produce one

2020-05-22T18:40:35.297300Z

async/transduce just doesn't do that step, so you have to do it yourself

ghaskins 2020-05-22T18:40:43.297700Z

i gotcha

ghaskins 2020-05-22T18:40:45.297900Z

let me try that

ghaskins 2020-05-22T18:45:39.298700Z

@hiredman that was it!

ghaskins 2020-05-22T18:45:40.298900Z

thank you

ghaskins 2020-05-22T18:45:54.299100Z

this worked:

(async/transduce (map :duration) summary (summary) ch)

1💪