how to understand alt in clojure core.async
Asked Answered
H

1

5

I have read the following document and the example, but still doesn't get what it really means. I understand alts!!, but not alt!!.Anybody show an example easy to understand?

https://clojure.github.io/core.async/#clojure.core.async/alt!!

I have also ready the following link

In Clojure (core.async) what's the difference between alts and alt?

update: The example is in the doc is:

(alt!
  [c t] ([val ch] (foo ch val))
  x ([v] v)
  [[out val]] :wrote
  :default 42)

for the second line of

[c t] ([val ch] (foo ch val))

channel-op of [c t] means a channel c and an value t: put value t on channel c. result-expr of ([val ch] (foo ch val)) means bidding [val ch] for the operation, but since it's a list, [val ch] should be evaluate as a function, and (foo ch val) will be as the parameter passed to function of [val ch]. but what does it mean for function of [val ch] with parameter of (foo ch val) ?

Heribertoheringer answered 18/1, 2016 at 13:44 Comment(3)
What part do you not understand?Fiendish
I think you're slightly off in your interpretation of the second line. [c t] is a vector of ports. [val ch] is a binding (as you said). ch is the channel that could be taken from; and val is the value taken . Function foo is then evaluated using that binding. See gist.github.com/konacaret/d1f5c19cfbe46d3b97a3 for a simple example of this.Silverside
That's very much for the example, it's very clear. Better to put it on the official doc :)Heribertoheringer
L
10

[c t] ([val ch] (foo ch val))

There are some cases where a list doesn't mean "evaluate as a function". For example:

(ns com.foo.bar
  (:require …))

In the above code, there is no function called :require being called.

Another example of lists being used for something other than function application is letfn:

(letfn [(foo [x] (+ (bar 1) x))
        (bar [x] (+ 2 x))]
  (+ (foo 5) (bar 7)))

As you can see above, letfn has two lists, one starting with the symbol foo, and one starting with the symbol bar, and neither of these lists is a traditional function call. Instead, letfn is defining two new functions, one with the name foo, and the other with the name bar. The rest of the expression is treated as a "body" in which to use those functions.

but what does it mean for function of [val ch] with parameter of (foo ch val) ?

Similar to letfn, alt defines a value named val and a channel named ch, and then the rest of the expression ((foo ch val)) is treated as a "body" in which to use those two names.

Explanation of alt!/alt!!:

I find it easiest to think of alt! and alt!! as being somewhat like cond, except instead of testing conditions to choose a body to execute, it waits on channels to choose a body to execute. Each clause consists of two parts, just like cond – the first part (the "channel op") is used to specify a channel that alt! should wait on, and the second part (the "result expr") specifies what should happen if that channel delivers a value first.

Since you probably want to access the value delivered by the channel or the channel itself when this happens, the result expr gives you the opportunity to bind both the value and the channel to symbols, and a body of code to execute with those bindings. Thus, the following clause…

[c t]
([val ch]
  (foo ch val))

…means:

One of the channel operations that this call to alt! should block on is an attempt to take from either of two channels, c or t. If either of those send a value before any other channel op in this call to alt!, then execute (foo ch val) with val bound to the value taken from the channel that first delivered a value, and with ch bound to the channel that delivered val (which will be either c or t).

and the following clause…

[[out input-val]]
([val ch]
  (bar ch val))

…means:

One of the channel operations that this call to alt! should block on is an attempt to put input-val onto a channel called out. If that succeeds before any other channel op in this call to alt!, then execute (bar ch val) with val bound to input-val and ch bound to out (the channel that successfully received the value).

Altogether, these two clauses would be written as:

(alt!
  [c t]        ; "Takes" can be a single channel instead of vectors.
  ([val ch]
    (foo ch val))

  [[out input-val]] ; "Puts" must be nested vectors.
  ([val ch]
    (bar ch val)))
Licketysplit answered 19/1, 2016 at 4:15 Comment(2)
Thanks erikprice, great explanation. One quesiont of [c t], are both c and t channel like konacaret show gist.github.com/konacaret/d1f5c19cfbe46d3b97a3? I read again the doc, if it's [[c t]], then c is a putting channel, and t is the value to put. If it's simply [c t], then both should be channel, am I right?Heribertoheringer
@DanielWu yes, you are right. I mistook the vector to indicate a "put" operation, but "put" requires nested vectors as you describe, and non-nested vectors are simply multiple channels to "take" from. I have updated the answer to reflect the correction so that it shows by "takes" and "puts".Licketysplit

© 2022 - 2024 — McMap. All rights reserved.