

ClojureScript Core.async Todos - brucehauman
http://rigsomelight.com/2013/07/18/clojurescript-core-async-todos.html

======
drcode
I dunno... the new async stuff is cool but I'm worried it's being oversold a
bit...

First of all, it is by nature a very "imperative" solution... it goes (to some
degree at least) counter to the whole idea that state changes need to be
limited as much as possible and isolated from the bulk of the "functional"
code.

Also, it seems people are using this idea to create little localized event
handlers, whereas before you'd solve this in a functional approach by having a
single global event handler.

Sure, having a global event handler can be derided by saying "this global
handler is spaghetti code because it doesn't isolate concerns."

But in the same way, this type of channel-driven programming can be called
spaghetti code because the many different goroutines can step on each other as
they manipulate the DOM.

For instance, if OP creates another channel-driven system for "editing todos"
he would have to do some hard thinking to make sure the "add todos" system and
the "edit todos" system don't interfere with each other and cause undefined
behavior.

I should point out I haven't built a system yet that is channel-based (I plan
to do this soon) so my knowledge of this approach is as of yet limited and
it's possible I will like channels better once I've used them more heavily.

~~~
brucehauman
Thanks for the thoughtful response.

It is interesting. In normal Js applications there is tremendous gravity
towards having mutable application state because the callbacks need a handle
of some sort (normally some instance of something). So I am wondering what you
mean by saying that the callback pattern is more functional?

In addition to mutable application state we have another global mutable state,
one that's even harder to opt out of: The Dom. Regardless wether we use
channels or callbacks this is going to be a problem whenever we have more than
one actor changing it, whether that is done at the same time or sequentially.

To me the place for core.async or the newish yield operator is that blocking
will allow us to wire together complex event behavior in some fairly
sophisticated ways. How long does it take to get a slideshow to really work
well in callback land? Or responsive autocomplete with correct lower and upper
bound timeout thresholds?

So we can handle the wiring of events and when we get to the right state we do
our functional application state transition.

Then as long as we are driving the Dom off of our application state then we
should be good right? * waiving my hands a bunch _

~~~
drcode
> what you mean by saying that the callback pattern is more functional?

I agree that the callback approach is not functional, and that the channel
approach is certainly much better than this status quo approach.

I'm comparing instead against an approach where you break down all relevant
events into JSON, and then push those into a global fully-functional "event
handler" function that translates events (along the "world state") into a set
of DOM manipulation primitives.

With that type of approach, no channels are necessary. I agree this approach
has difficulties with time-sensitive operations (such as responsive
autocomplete, etc) and that channels offer advantages for this.

I'm not yet sure which approach leads to the least tradeoffs or whether there
is a third approach that combines the best of both worlds that I don't quite
comprehend yet.

> another global mutable state, one that's even harder to opt out of: The Dom

Well, I think the success of angular.js shows that it's possible (and probably
desirable) to completely isolate DOM state changes from other program code...
though the OP program doesn't do this, this is clearly just a simplified
example, so I want to see what is possible once someone builds a client-side
framework that is built on channels...

~~~
gruseom
_I 'm comparing instead against an approach where you break down all relevant
events into JSON, and then push those into a global fully-functional "event
handler" function that translates events (along the "world state") into a set
of DOM manipulation primitives_

How common is this approach? I'd be very interested to hear people's
experiences with it. It's what we do, except we run the global event processor
on a JS timer so that it works like a game loop picking items off a queue. Our
DOM event handlers (the things you call addEventListener with) are all trivial
stubs that simply create JSON descriptions of events as they happen and append
them to that queue. We tried many different approaches before we hit on this,
and it works surprisingly well in the two hardest areas: managing complexity
and keeping the UI snappy.

You can also create perceived concurrency this way: you do a little work on an
item in the queue and then, if it's not done, put it back at the back of the
line. That's how we do the "time-sensitive operations" you mention.

I'm not sure what to call this design. I think of it as a state machine, but
that's a metaphor rather than a precise description.

~~~
abecedarius
Are you familiar with E? It basically works like this, plus built-in,
transparent promises. [http://www.erights.org/elib/concurrency/event-
loop.html](http://www.erights.org/elib/concurrency/event-loop.html) outlines
why you might settle on this model. The messages about coroutines linked at
the bottom argue against things like core.async. (This all long precedes
core.async, but Concurrent ML had that kind of design.)

I've only done simple things in JS and don't understand what advantage you're
getting by queueing events yourself, since JS already runs an event loop. Is
it that you can inspect the queue and drop/modify events superseded by
subsequent ones? I tried to do that once and it didn't help, but I've gotta
admit I'm the opposite of an expert on JS UIs.

~~~
gruseom
Thanks for the link! I shall read it.

I wrote a reply that is perhaps a bit too long to put here, so I put it at
[http://pastebin.com/4nN04Hj3](http://pastebin.com/4nN04Hj3) instead.

The short answers are: (1) this event loop is different from the browser's
event loop because it's app-specific. Having a single interception point where
you translate the browser event stream (mousedown, keypress etc.) into higher-
level app-specific terms, and then write the rest of the UI in the latter, is
a strategy for reducing complexity—an application of bottom-up programming to
web UIs, really; and (2) emphatically yes, making the "official" event loop
trivial and running all the complex stuff off your own app-specific loop
allows you to control how much processing to do and when, which can be a way
out of some thorny performance problems (such as sluggish scrolling) as well
as an easy path to simulated concurrency.

It's interesting that you should have said "drop/modify events superseded by
subsequent ones" because that's precisely what led us to this design
originally—we were desperate, in fact, for a way to do that. The bottom-up
programming aspect and the concurrency aspect only became clear later.

~~~
abecedarius
I think I read that E's promises came out of experience with UI programming
too -- that they invented
[http://erights.org/elib/distrib/pipeline.html](http://erights.org/elib/distrib/pipeline.html)
for the sake of sanely programming snappy UIs for Xanadu.

Sounds like I gave up too easily on queuing and pruning events, or it was the
particular platform (touch tracking on Nexus 7 and first-gen iPad).

Thanks for the experience report. :-) It might help me remember this when I
get back to
[http://wry.me/hacking/lissajous.html](http://wry.me/hacking/lissajous.html)
to finish & polish -- I let it get all cut-and-pastey in doing drag-controls
for the first time.

~~~
gruseom
I've now read those pages. I knew about E for its capability model but not at
all for its concurrency model, which is indeed very interesting (more
interesting, to me) and close to home. There's a presentation mentioned there
that I found slightly clearer than the page itself; the link is dead but it
can be read here:

[http://web.archive.org/web/20070626045558/http://www.drjava....](http://web.archive.org/web/20070626045558/http://www.drjava.de/e-presentation/html-
english/img0.html)

This model deserves to better known, especially since all this stuff is in
play again nowadays. Are there other implementations of it? (Node.js might be
a natural platform, given its event loop and the popularity of promises
there.) I also wonder what the biggest differences are between it and Erlang.

In your pipelining example (t3 := (x <\- a()) <\- c(y <\- b())), what happens
if the messages arrive out of order? (Or does the system prevent that and if
so how?) I suppose if an expression can't be evaluated because a promise
hasn't resolved yet, it could just go back to the end of the queue and
eventually everything will bubble up?

Also: _that_ Xanadu? Wow, no idea.

Edit: I forgot another thing I wanted to include in this omnibus comment. The
paper "On the Development of Reactive Systems" that's mentioned at your first
link and which can be found at [1], starts off with a very interesting and
exciting distinction between "transformational" systems (which take inputs and
transform them into outputs) and "reactive" systems that "are repeatedly
prompted by the outside world and [whose] role is to continuously respond to
external inputs. A reactive system, in general, does not compute or perform a
function, but is supposed to maintain a certain ongoing relationship, so to
speak, with its environment". I think this is extraordinarily lucid and that
the authors are right to talk about concurrency—and probably complexity in
general—in the latter sort of system as being a different challenge than in
the former. Unfortunately, the paper quickly sinks into a pit of software
process goo and never returns. A little Googling gives me the impression that
important technical work did come out of it, though, and if anyone knows what
the high points are I would like to see them.

[1]
[http://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Reac...](http://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/ReactiveSystems.pdf)

~~~
abecedarius
Yes, that Xanadu!

The VatTP protocol is meant to guarantee 'just enough' ordering as at
[http://www.erights.org/elib/concurrency/partial-
order.html](http://www.erights.org/elib/concurrency/partial-order.html) \--
there was a talk about VatTP, with slides, but I haven't seen a proper write-
up and never learned it.

I think
[http://ai.eecs.umich.edu/~tpkelly/Ken/](http://ai.eecs.umich.edu/~tpkelly/Ken/)
is the most active descendant (and V8Ken linked from there which they intend
to hook up with node.js). Daira Hopwood's new language Noether sounds
promising: [https://thestrangeloop.com/sessions/noether-a-concurrent-
sec...](https://thestrangeloop.com/sessions/noether-a-concurrent-security-
oriented-language)

About Erlang, besides capabilities and mutable data I think the biggest
difference comes from blocking receive. This gives it more of a CSP flavor. My
own experience is limited -- my exposure to Erlang was mostly helping other
hackers. (I also made an Erlang-influenced Scheme dialect I was unsatisfied
with for boring reasons, but it was kind of in between: immutable data,
channels as capabilities, synchronous messages.)

------
jrobn
I've been slowly migrating my JavaScript code to clojurescript and I haven't
wanted to touch JS since. Apart from requiring a build step to generate JS,
clijurescript is the browser language I always wanted. JS seems like such a
hack.

~~~
mrbrowning
Have you settled on a good workflow for ClojureScript projects? cljsbuild at
least lets you amortize JVM startup time with the _auto_ mode, but the Write-
Compile-Test loop (as Joel Spolsky calls it) still feels a little more
sluggish than what I'm used to in other contexts.

~~~
swannodette
They need more work but I use browser REPL or Bodil Stokke's Node REPL
[http://github.com/bodil/cljs-noderepl](http://github.com/bodil/cljs-noderepl)
if I need to do an intensive session of interactive coding without refreshing
or waiting for recompiles. I contribute a bit to core.async now and it's
definitely a goal to make sure that core.async works transparently whether
you're coding against the browser or Node.

------
krosaen
Two first impressions:

    
    
        - whoa, this is cool!
        - this is harder to read than the callback approach right now
    

Makes me want to dig deeper, fully grok and reevaluate whether or not I'd
really prefer this approach to callbacks. I have similar mixed feelings to
using twisted vs tornado in python.

There's no doubt channel support in clojure is awesome, just a personal matter
of whether I'll end up preferring this paradigm for UI development.

~~~
swannodette
I would liken it to the Game of Go (pun intended). You have a very small
number of elements: chan, go, >!, <!, alts!. The learning curve is a bit steep
because you have invest some time _unlearning_ patterns you've picked up from
traditional UI programming. But once you cross over the hump and you realize
what is possible with just these few tools a whole vista of possibilities
opens up. I'm sure the fans of go-lang would agree.

Another analogy - Alan Kay compares software engineering to architecture and
how so much engineering is brute force (think pyramids). core.async (really
CSP) is like an arch (think gothic cathedral). Common UI patterns no longer
seem arduous - and UI patterns which once seemed far out reach can be
accomplished with a reasonable amount of engineering effort.

~~~
krosaen
Thanks - I like those analogies. I want to invest some time to see if the
unlearning works for me. Most times when converting to a higher level way of
doing something it eventually does.

Also - thanks for all the awesome core.async gists - I feel like I want to
take a geek sabbatical just so I can study them :)

------
ghempton
When it comes to UI programming, I tend towards declarative solutions to
problems over imperative ones.

That said, this feels like an imperative solution to something that a state
machine could handle equally well.

~~~
swannodette
... core.async compiles down to _efficient_ state machines that I have
absolutely no desire to write by hand.

------
SeanDav
Can this be used for node.js development? If so, any example projects out
there?

~~~
pjmlp
In regards to Clojure, why bother with node.js when one has the full power of
the JVM and CLR at disposal?!

~~~
emidln
Because you want to use a library that was written for Node and not speed time
finding (or writing) something similar for the JVM? If you know Node better
than the JVM, Clojurescript on Node is very likely more valuable to you when
starting out than Clojure on the JVM.

~~~
pjmlp
Given JVM and CLR's age, I doubt there is any library for node that doesn't
already exist in a similar way for the other platforms.

~~~
emidln
Just because a library exists for the JVM or CLR doesn't mean a person
familiar with Node knows that it exists or the advantages/disadvantages of the
15 libraries that will show up for any given thing they are trying to do. The
libraries, profiling tools, performance tuning details. These are the things
that make up a platform. Knowing the syntax of Clojure and nothing about the
JVM ecosystem isn't going to help you ship any faster. Should a developer in
this situation bite the bullet and learn a better supported platform? Maybe.
Does everyone have time to do so? Probably not.

~~~
pjmlp
Since Clojure was born in the JVM, I doubt anyone learning Clojure will be
more at home in node.

