API

Note

Reagents.jl is still work-in-progress.

Reagents

Reagents.ReagentType
Reagents.Reagent

Composable description for nonblocking and synchronous operations.

Reagents can be composed with although it is recommended to use the opposite composition operator . A composed Reagent can be called, just like a function, to actually execute the side-effects.

Example

julia> using Reagents

julia> reagent = Reagents.Return(1) ⨟ Reagents.Map(string);

julia> reagent()
"1"
source
Reagents.CASType
Reagents.CAS(ref::Reagents.Ref{T}, expected::T, desired::T)

A reagent for replacing the value in ref from expected to desired. Multiple CAS reagents are committed atomically.

Example

julia> using Reagents

julia> ref1 = Reagents.Ref(111);

julia> ref2 = Reagents.Ref(222);

julia> reagent = Reagents.CAS(ref1, 111, -1) ⨟ Reagents.CAS(ref2, 222, -2);

julia> reagent();

julia> ref1[]
-1

julia> ref2[]
-2
source
Reagents.ComputedType
Reagents.Computed(f)

Create a reagent dynamically with function f which receives the output of the upstream reagent. The resulting reagent is composed with the downstream reagent.

Examples

julia> using Reagents
       using Reagents: Read, CAS, Computed

julia> ref = Reagents.Ref(0);

julia> reagent = Read(ref) ⨟ Computed(old -> CAS(ref, old, old + 1));

julia> reagent();

julia> ref[]
1

julia> reagent();

julia> ref[]
2
source
Reagents.IdentityType
Reagents.Identity()

The identity reagent. This is the identity element of (hence of ).

Examples

julia> using Reagents

julia> reagent = Reagents.Map(string) ⨟ Reagents.Identity();

julia> reagent(111)
"111"
source
Reagents.MapType
Reagents.Map(f)

Transform the output value of the upstream reagent by f and pass it to the downstream reagent.

Examples

julia> using Reagents

julia> ref = Reagents.Ref(111);

julia> (Reagents.Read(ref) ⨟ Reagents.Map(string))()
"111"
source
Reagents.PostCommitType
Reagents.PostCommit(f)

Run f(x) when the reagent successfully completed its reaction where x is the output of the upstream reagent.

Examples

julia> using Reagents

julia> ref = Reagents.Ref(111);

julia> reagent = Reagents.Read(ref) ⨟ Reagents.PostCommit(x -> @show(x));

julia> reagent();
x = 111
source
Reagents.ReadType
Reagents.Read(ref::Reagents.Ref)

A reagent that reads the value in ref.

Example

julia> using Reagents

julia> ref = Reagents.Ref(111);

julia> reagent = Reagents.Read(ref);

julia> reagent()
111
source
Reagents.ReturnType
Reagents.Return(value)

Pass value to the downstream reagent.

Examples

julia> using Reagents

julia> Reagents.Return(1)()
1

julia> ref = Reagents.Ref(111);

julia> (Reagents.Return(222) ⨟ Reagents.Update((old, inc) -> (old + inc, old), ref))()
111

julia> ref[]
333
source
Reagents.UntilType
Reagents.Until(f, loop::Reagent)

Keep reacting with reagent while f returns nothing on the output of loop. The reaction of loop will be committed if f returns nothing but without the reaction of the reagent upstream to Until; i.e., they are still put on hold. Once f returns non-nothing, the upstream reaction (from reagents before the loop), the reaction of the loop, and the downstream reactions are committed together, like other type of reagents.

Examples

julia> using Reagents

julia> ref1 = Reagents.Ref(10);
       ref2 = Reagents.Ref(111);

julia> reagent = Reagents.Until(Reagents.Update((x, _) -> (x - 1, x), ref1)) do x
           if x > 1
               nothing
           else
               Some(x)
           end
       end ⨟ Reagents.Update((x, _) -> (x + 1, x), ref2);

julia> reagent()
111

julia> ref1[]
0

julia> ref2[]
112
source
Reagents.UpdateType
Reagents.Update(f, ref::Reagents.Ref{T})

Given a function of form (v::T, a) -> (w::T, b) and a Reagents.Ref holding a value of type T, update its value to w and pass the output b to the downstream reagent. The function f receives the value v in ref and the output of a of the upstream reagent.

Example

julia> using Reagents

julia> ref = Reagents.Ref(0);

julia> add! = Reagents.Update((v, a) -> (v + a, v), ref);

julia> add!(1)  # add 1 and return the old value
0

julia> add!(2)
1

julia> ref[]
3
source
Reagents.WithNackType
Reagents.WithNack(f)

Dynamically create a reagent with negative acknowledgement (nack) reagent.

Function f takes a reagent nack that is blocked until the reaction of this reagent is cancelled (i.e., another branch of | is selected). Function f must return a reagent.

source
Reagents.channelFunction
Reagents.channel(A::Type, B::Type = A) -> (a2b, b2a)

Create a pair of swap endpoints for exchanging a value a of type A and a value b of type B synchronously.

The endpoints a2b and b2a are reagents.

Example

julia> using Reagents

julia> a2b, b2a = Reagents.channel(Int, Symbol);

julia> t = @async b2a(:hello);

julia> a2b(123)
:hello

julia> fetch(t)
123
source

Reagent Combinators

Reagents.:|Function
r1 | r2

The choice combinator. Invokes the reaction of one and exactly one reagent. The choice is left-biased; i.e., r1 | r2 tries r1 first and then try r2.

Example

julia> using Reagents

julia> ref1 = Reagents.Ref(111);

julia> ref2 = Reagents.Ref(222);

julia> r1 = Reagents.CAS(ref1, -1, 0);  # will fail (wrong expected old value)

julia> r2 = Reagents.CAS(ref2, 222, 333);  # will succeed

julia> (r1 | r2)();

julia> ref1[]
111

julia> ref2[]
333
source
Reagents.:&Function
r1 & r2

The pairing combinator. Both reagents r1 and r2 are executed and the downstream reagent receives the tuple (y1, y2) where y1 is the output of r1 and y2 is the output of r2.

Example

julia> using Reagents

julia> ref1 = Reagents.Ref(111);

julia> ref2 = Reagents.Ref(222);

julia> r1 = Reagents.Read(ref1);

julia> r2 = Reagents.Read(ref2);

julia> (r1 & r2)()
(111, 222)
source
CompositionsBase.:⨟Function
r1 ⨟ r2
r2 ∘ r1
opcompose(r1, r2)
compose(r2, r1)

The operator (\bbsemi) sequences reagents. The composition r1 ⨟ r2 means to invoke r1 and also r2 with the output of r1. Note that the entire reaction is still done atomically.

The syntaxes r1 ⨟ r2 and r2 ∘ r1 are equivalent. It is recommended to use r1 ⨟ r2 to clarify the top-to-down data flow when the reagent definition spans multiple lines. However, knowing that r1 ⨟ r2 is equivalent to r2 ∘ r1 can be useful for remember that, in the reaction (r2 ∘ r1)(x), the reagent r1 is the one that sees the input x.

opcompose and compose are ASCII aliases of and , respectively.

Example

julia> using Reagents

julia> ref = Reagents.Ref(111);

julia> r1 = Reagents.Read(ref);

julia> r2 = Reagents.Map(string);

julia> (r1 ⨟ r2)()
"111"
source

Reaction failures

Misc

Reagents.RefType
Reagents.Ref{T}()
Reagents.Ref{T}(value::T)

Create a reference storing a value of type T.

Use reagents such as Reagents.Update, Reagents.CAS, and Reagents.Read to manipulate the value.

Example

julia> using Reagents

julia> ref = Reagents.Ref{Int}();

julia> ref[] = 111;

julia> ref[]
111

julia> Reagents.try!(Reagents.CAS(ref, 111, 222)) !== nothing
true

julia> ref[]
222
source
Reagents.dissolveFunction
Reagents.dissolve(catalyst::Reagent)

Register catalyst as a persistent reagent that helps other reaction.

The reagent catalyst must include a blocking reagent (i.e., Reagents.channel).

For more information, see Catalysts section in the manual.

Examples

julia> using Reagents

julia> send1, receive1 = Reagents.channel(Int, Nothing)
       send2, receive2 = Reagents.channel(Char, Nothing)
       sendall, receiveall = Reagents.channel(Tuple{Int,Char}, Nothing);

julia> catalyst = (receive1 & receive2) ⨟ sendall;

julia> Reagents.dissolve(catalyst);

julia> @sync begin
           @async send1(1)
           @async send2('a')
           receiveall()
       end
(1, 'a')
source
Reagents.try!Function
Reagents.try!(reagent::Reagent, [value = nothing])

Invoke reagent with value. If it succeeds with an output, return Some(output). If it fails to react, return nothing.

It is guaranteed to return even if reagent is unsatisfiable (e.g., Reagents.CAS(Reagents.Ref(0), 1, 2)).

source
Reagents.trysync!Function
Reagents.trysync!(reagent::Reagent, [value = nothing]) -> Some(output) or nothing

Invoke synchronizing reagent with value. If it succeeds with an output, return Some(output). If it is blocked, return nothing.

This function is guaranteed to return if the reagent eventually succeeds or synchronizes. Supplying unsatisfiable reagent (e.g., Reagents.CAS(Reagents.Ref(0), 1, 2)) is unsupported (it is likely to deadlock).

source