Fork me on GitHub
#core-async
<
2020-05-22
>
jjttjj15:05:26

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?

ghaskins18:05:54

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

ghaskins18:05:06

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

ghaskins18:05:33

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

ghaskins18:05:51

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

ghaskins18:05:18

any guidance appreciated

hiredman18:05:04

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

hiredman18:05:30

(the channel is closed nowhere to put it)

hiredman18:05:33

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

hiredman18:05:09

(a transducer in a channel?)

ghaskins18:05:57

that makes some sense

ghaskins18:05:35

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

ghaskins18:05:12

@hiredman My first attempt looks like this

(async/transduce :duration kixi/summary ch)

ghaskins18:05:22

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

ghaskins18:05:28

but im not clear what I need for init

hiredman18:05:04

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

hiredman18:05:45

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

hiredman18:05:22

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

hiredman18:05:33

xform is a transducer

hiredman18:05:37

f is a reducing function

hiredman18:05:53

init is the initial value for the accumulator when reducing

hiredman18:05:59

ch is the source of data

ghaskins18:05:48

right...if I run it like this

ghaskins18:05:50

(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}

ghaskins18:05:52

it makes sense to me

ghaskins18:05:02

im not sure how to map that to async/transduce

hiredman18:05:07

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

ghaskins18:05:56

i guess this is closer to my use case

ghaskins18:05:57

(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}

hiredman18:05:40

if you look at the docstring for transduce:

hiredman18:05:45

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.

hiredman18:05:10

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

hiredman18:05:35

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

ghaskins18:05:45

let me try that

ghaskins18:05:54

this worked:

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

💪 4