API
Reagents.jl is still work-in-progress.
ReagentsReagents.BlockReagents.CASReagents.ComputedReagents.IdentityReagents.MapReagents.PostCommitReagents.ReadReagents.ReagentReagents.RefReagents.RetryReagents.ReturnReagents.UntilReagents.UpdateReagents.WithNackCompositionsBase.:⨟Reagents.:&Reagents.:|Reagents.channelReagents.dissolveReagents.try!Reagents.trysync!
Reagents
Reagents.Reagent — TypeReagents.ReagentComposable 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"Reagents.CAS — TypeReagents.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[]
-2Reagents.Computed — TypeReagents.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[]
2Reagents.Identity — TypeReagents.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"Reagents.Map — TypeReagents.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"Reagents.PostCommit — TypeReagents.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 = 111Reagents.Read — TypeReagents.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()
111Reagents.Return — TypeReagents.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[]
333Reagents.Until — TypeReagents.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[]
112Reagents.Update — TypeReagents.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[]
3Reagents.WithNack — TypeReagents.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.
Reagents.channel — FunctionReagents.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)
123Reagent Combinators
Reagents.:| — Functionr1 | r2The 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[]
333Reagents.:& — Functionr1 & r2The 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)CompositionsBase.:⨟ — Functionr1 ⨟ 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"Reaction failures
Reagents.Block — TypeReagents.Block()A value indicating that the reaction is blocked.
Reagents such as Reagents.Computed and Reagents.Map can return this value to indicate that other branches of the choice combinator | should be used.
Reagents.Retry — TypeReagents.RetryA value indicating that the reaction should be retried.
Misc
Reagents.Ref — TypeReagents.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[]
222Reagents.dissolve — FunctionReagents.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')Reagents.try! — FunctionReagents.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)).
Reagents.trysync! — FunctionReagents.trysync!(reagent::Reagent, [value = nothing]) -> Some(output) or nothingInvoke 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).