core.async and referential transparency

495 views
Skip to first unread message

bertschi

unread,
Sep 3, 2013, 10:00:13 AM9/3/13
to clo...@googlegroups.com
Hi,

recently I got interested core.async (also thanks to great blog post such as the ones by David Nolen) and wanted to understand better how it compares to other reactive libraries, in particular coming from the research on Functional Reactive Programming (FRP).

Compared to FRP, core.async appears to be build around side-effects on channels which breaks referential transparency. What I mean here, is that channels are somewhat like reference types and connect a (time varying) value to an identity.

As an example consider a map-like channel transformer (as found in many posts explaining core.async) (map-ch f in out) which takes values x from the input channel in and writes (f x) to the output channel out.

(defn map-ch
  ([f in]
     (map-ch f in (chan)))
  ([f in out]
    (go (loop []
             (if-let [x (<! in)]
                (do (>! out (f x))
                     (recur))
                (close! out))))
    out))

Now, the following two snippets behave rather differently:

(let [out-1 (map-ch inc (<some function creating a channel and writing data to it>))
      out-2 (map-ch dec (<some function creating a channel and writing data to it>))]
  (go (loop []
          (when-let [x (<! out-1)]
              (println "receiving " x)
              (recur)))))

(let [in     (<some function creating a channel and writing data to it>)
      out-1 (map-ch inc in)
      out-2 (map-ch dec in)]
  (go (loop []
          (when-let [x (<! out-1)]
              (println "receiving " x)
              (recur)))))

Whereas the first example receives all incremented values, the second one receives potentially less since both consumers read from the very same input channel! This also means that one can break working code, by (accidently) attaching an additional consumer ... note that in Rx I can subscribe as many times as I want to an observable without any effect on the rest of the data flow.

Maybe I'm misunderstanding CSP and how it allows to compose and synchronize processes, but somehow I feel that channels complect the idea of state varying values and identity. Regarding that referential transparency, which is required for equational reasoning, is one of the best properties of purely functional programs, breaking  it seems to be at least problematic.

Any thoughts?

   Nils

Timothy Baldridge

unread,
Sep 3, 2013, 10:17:43 AM9/3/13
to clo...@googlegroups.com
What you are describing is a pub sub type system. This could be built on top of core.async, but it doesn't have to change the core library at all. 

But yes, you are mis-understanding the main use-case for a library like core.async. The library was created to allow for the building of systems that consist of queues and workers that pull items from those queues. As Rich explained it a few times, consider an assembly plant. Workers take items from conveyor belts, perform some work and place them on other belts. In this use case, duplicating items and giving a copy to each worker doesn't make a whole lot of sense. 

Timothy


--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--
“One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.”
(Robert Firth)

David Nolen

unread,
Sep 3, 2013, 10:20:19 AM9/3/13
to clojure
On Tue, Sep 3, 2013 at 10:00 AM, bertschi <nils.ber...@googlemail.com> wrote:
Whereas the first example receives all incremented values, the second one receives potentially less since both consumers read from the very same input channel! This also means that one can break working code, by (accidently) attaching an additional consumer ... note that in Rx I can subscribe as many times as I want to an observable without any effect on the rest of the data flow.

Which also creates a resource problem as a result of the subscription side effect. You can build subscription based on Rx over core.async if you like, but I think it's more of a tradeoff than people realize.
 
Maybe I'm misunderstanding CSP and how it allows to compose and synchronize processes, but somehow I feel that channels complect the idea of state varying values and identity. Regarding that referential transparency, which is required for equational reasoning, is one of the best properties of purely functional programs, breaking  it seems to be at least problematic

Problematic in what real sense? Haskell provides similar facilities over the Chan data type. Clearly they also believe it's useful to model problems in this way.

David 

David Nolen

unread,
Sep 3, 2013, 10:28:35 AM9/3/13
to clojure
On Tue, Sep 3, 2013 at 10:00 AM, bertschi <nils.ber...@googlemail.com> wrote:
Whereas the first example receives all incremented values, the second one receives potentially less since both consumers read from the very same input channel! This also means that one can break working code, by (accidently) attaching an additional consumer ... note that in Rx I can subscribe as many times as I want to an observable without any effect on the rest of the data flow.

I will also say this fear of losing information is ignoring an important aspect of non-toy CSP systems. There are many cases where you absolutely have no choice - you must drop information (and preferably be able to specify how) if you do not want your program to fall apart. This is why Haskell has BoundedChan, Go has buffer backed channels, and why core.async is even more flexible about the dropping semantics.

David

bertschi

unread,
Sep 3, 2013, 10:47:34 AM9/3/13
to clo...@googlegroups.com
Hi,

thanks for your remarks.

@Timothy: Yes, looks like I mis-understand the main motivation for core.async or I'm just not used to see my programs as an assembly like sending data boxes around ;-)


On Tuesday, September 3, 2013 4:20:19 PM UTC+2, David Nolen wrote:
Which also creates a resource problem as a result of the subscription side effect. You can build subscription based on Rx over core.async if you like, but I think it's more of a tradeoff than people realize.

Correct, the producer can easily outrun the consumer in Rx ... appears to be somewhat similar to holding the head space-leaks in lazy sequences, but probably much more subtle to detect and fix.
 
Problematic in what real sense? Haskell provides similar facilities over the Chan data type. Clearly they also believe it's useful to model problems in this way.
 
As far as I know, Haskell has Chan data types in its concurrency extensions, but I have never seen them in FRP. Maybe this means that FRP is addressing a different problem. On the other hand, the Automaton Arrow can be used to implement state machines that transition on receiving inputs in a composable and referentially transparent way ... but they still have some problems regarding efficiency and space-leaks.
I was just wondering, how core.async relates to FRP and like that the latter allows for equational reasoning about your data flow. In the end, side-effects have to occur somewhere since callbacks/event handlers/etc. can never return values, so FRP has to deal with similar problems (currently it looks like the higher abstraction provides less control about resources).

Best,

    Nils
 
David 

David Nolen

unread,
Sep 3, 2013, 11:28:13 AM9/3/13
to clojure
On Tue, Sep 3, 2013 at 10:47 AM, bertschi <nils.ber...@googlemail.com> wrote:
As far as I know, Haskell has Chan data types in its concurrency extensions, but I have never seen them in FRP. Maybe this means that FRP is addressing a different problem. On the other hand, the Automaton Arrow can be used to implement state machines that transition on receiving inputs in a composable and referentially transparent way ... but they still have some problems regarding efficiency and space-leaks.

referential transparency without efficiency with space leaks is not what I call a path towards correctness. I am sure pure FRP implementations will one day widely surmount these obstacles towards correctness. I'm personally not going to wait - a tunable CSP model seems better suited to the construction of correct (in the sense that it actually reliably works for the end user, not proof) interactive programs.

That said core.async is not anti-FRP, most of the code that I've written using core.async leverages an FRP style quite liberally, but it's refreshing to not be ball-and-chained to that approach when writing interactive programs.

David

bertschi

unread,
Sep 3, 2013, 12:12:26 PM9/3/13
to clo...@googlegroups.com
Hi David,


On Tuesday, September 3, 2013 5:28:13 PM UTC+2, David Nolen wrote:
On Tue, Sep 3, 2013 at 10:47 AM, bertschi <nils.ber...@googlemail.com> wrote:
As far as I know, Haskell has Chan data types in its concurrency extensions, but I have never seen them in FRP. Maybe this means that FRP is addressing a different problem. On the other hand, the Automaton Arrow can be used to implement state machines that transition on receiving inputs in a composable and referentially transparent way ... but they still have some problems regarding efficiency and space-leaks.

referential transparency without efficiency with space leaks is not what I call a path towards correctness. I am sure pure FRP implementations will one day widely surmount these obstacles towards correctness. I'm personally not going to wait - a tunable CSP model seems better suited to the construction of correct (in the sense that it actually reliably works for the end user, not proof) interactive programs.

thanks for your insightful comments. I perfectly agree that FRP is not yet there (and maybe never will given its ambitious goals) and core.async provides a nice and WORKING solution. I certainly do not want to criticize or even argue against core.async which is indeed a very nice library (the power of code walking has always impressed me :-)).
 
That said core.async is not anti-FRP, most of the code that I've written using core.async leverages an FRP style quite liberally, but it's refreshing to not be ball-and-chained to that approach when writing interactive programs.
Good to know and FRP certainly has some good ideas which could be exploited on top of core.async. Maybe one has to give up some of the theoretical niceties in order to reach an efficient and resource controlled data flow model. I will try to read some more about CSP and see if I get the programming model that it employs ...

Best,

    Nils

 
David

Sean Corfield

unread,
Sep 3, 2013, 1:49:25 PM9/3/13
to clo...@googlegroups.com
Just an aside, Nils, have you take a look at Elm? It's a Haskell-like
language, designed for FRP, compiles to JS, and deliberately avoids
monads and arrows (at least in terminology) by having Signal and
Automaton abstractions... http://elm-lang.org
> --
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your
> first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to clojure+u...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.



--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

bertschi

unread,
Sep 4, 2013, 8:31:21 AM9/4/13
to clo...@googlegroups.com
Hi Sean,

thanks for the link, but I did look at Elm before and read the papers a couple month ago ... as far as I remember the implementation somewhat follows FrTime/Flapjax, but with an additional async expression, which does not prevent glitches.

Best,

    Nils 

On Tuesday, September 3, 2013 7:49:25 PM UTC+2, Sean Corfield wrote:
Just an aside, Nils, have you take a look at Elm? It's a Haskell-like
language, designed for FRP, compiles to JS, and deliberately avoids
monads and arrows (at least in terminology) by having Signal and
Automaton abstractions... http://elm-lang.org

Reply all
Reply to author
Forward
0 new messages