Contexts
Why Contexts?
Contexts are an interesting approach to achieve dynamic behavior of objects. Let's think of a program, controling a robot. A robot may act differently when a human is nearby or not. Another example is an energy system that should behave differently on a sunny summer day than on a cloudy and snowy winter day. The context, in which such a program runs could be defined as sunnyDay, warmWeather or humanNearby. It describes the properties of the environment or situation in which the software runs.
Context Definition in Contexts.jl
In Contexts.jl, Context is an abstract type.
Contexts.Context — Type
Context <: AbstractContextAbstract supertype for all concrete context types.
By defining a new Context, a new struct <ContextName>ContextType will be created as a subtype of Context:
struct <ContextName>ContextType <: Context endand then, a singleton object of this type is defined:
<ContextName> = <ContextName>ContextType()In that way, contexts are available as arguments of methods to be utilized by the multiple dispatch.
Context Usage
Defining new Contexts with @newContext Macro
To create a new context, use the @newContext macro:
@newContext Context1Contexts.@newContext — Macro
@newContext(contextName)Macro to define a new context type and singleton instance. Accepts a String, Symbol, or an expression of multiple names. Creates a struct subtype of Context and registers it.
Example: @newContext("MyContext") @newContext(AnotherContext) @newContext FirstContext, SecondContext
Context-dependent Behavior with the @context Macro
Since Context1 is now available as a variable representing a value of the context-specific type Context1ContextType, it can be used in function arguments and using the multiple dispatch, context-dependent behavior is realized. For easier Syntax, the macro @context is defined. Depending on its usage, it fulfills different tasks.
Calling @context on a function definition will define a context depening function, by adding an attribute of type Context1ContextType as the first function argument:
@context Context1 function HelloWorld()
println("Hello World in Context 1!")
end @context Context2 function HelloWorld()
println("Hello World in Context 2!")
end Calling @context on a function (or macro) call will add the context as the first attribute.
@context Context1 HelloWorld()will therefore print Hello World in Context 1! and
@context Context2 HelloWorld()will print Hello World in Context 2!. This also means that executing HelloWorld(Context1) would also print Hello World in Context 1!. Also the variable context is available in functions.
Contexts.@context — Macro
@context(cname, expr)Macro to inject context information into a function or macro call or function definition. Adds context type or name as an argument to the function or macro.
A generic context type can be specified with Any.
Arguments:
cname: Context name or typeexpr: Function, macro call, or function definition
Example: @context MyContext function myfunc(x::myType) ... end @context Any function myfunc(x::myType) ... end @context MyContext myfunc(x)
It is also possible to use a tuple of multiple contexts:
@context (Context1, Context2) HelloWorld()Activeness of Contexts
When programming with contexts, you might want to define all possible contexts at the beginning, but only some of them are active at the same time. Therefore the contexts in Contexts.jl can be activated and deactivated by
Contexts.activateContext — Function
activateContext(context::T) where {T <: Context}Activates a context and runs compiled PetriNet.
Contexts.deactivateContext — Function
deactivateContext(context::T) where {T <: Context}Deactivates a context and runs compiled PetriNet.
After the definition of a context, the context will be deactive. When a clean start is needed, the function
deactivateAllContexts()can help.
Note, that functions can of course also run when a deactivated context is handed as a function argument. The activeness of a context can be checked with:
Contexts.isActive — Method
isActive(context::T) where {T <: Context}Checks if a context is currently active. Returns true if active, false otherwise.
This can be used in if-statements to avoid calling functions with deactivated contexts or to have varying functionality depending on activeness.
@context Context1 function printContext()
isActive(context) ? println("Context is active") : println("Context is not active")
endThe macro @activeContext only calls the function if the context is active:
Contexts.@activeContext — Macro
@activeContext(cname, expr)Macro to conditionally execute a function or macro call only if the context is active. Wraps the call in an if isActive(context) block.
Arguments:
cname: Context name or typeexpr: Function or macro call
Example: @activeContext(MyContext, myfunc(x))
A more conventient way to work with contexts and context-specific functions is via context groups and state machines, see ContextGroup and ContextStateMachine.
As explained in Context Modeling, activating contexts will run Petri nets that check the compliance of the contexts' activeness with constraints. However, it is also possible to (de-)activate contexts without the Petri net. This can be helpful to reach valid initial states.
Contexts.activateContextWithoutPN — Function
activateContextWithoutPN(context::T) where {T <: Context}Activates a context without running PetriNet logic. Mainly used for internal. Should be used with caution
Contexts.deactivateContextWithoutPN — Function
deactivateContextWithoutPN(context::T) where {T <: Context}Deactivates a context without running PetriNet logic. Mainly used for internal. Should be used with caution
(de)activating contexts without Petri nets can lead to reaching invalid states that are not correctly healed after running the PN.
Boolean Expressions on Contexts
The |, & and ! operators can be used to create Boolean expressions on Contexts.
Contexts.OrContextRule — Type
OrContextRule <: AbstractContextRuleRepresents a logical OR between two contexts or context rules.
Fields:
c1,c2: Contexts or context rules.
Example: OrContextRule(ctx1, ctx2)
Contexts.AndContextRule — Type
AndContextRule <: AbstractContextRuleRepresents a logical AND between two contexts or context rules.
Fields:
c1,c2: Contexts or context rules.
Example: AndContextRule(ctx1, ctx2)
Contexts.NotContextRule — Type
NotContextRule <: AbstractContextRuleRepresents a logical NOT of a context or context rule.
Fields:
c: Context or context rule.
Example: NotContextRule(ctx)
Those expressions can be checked for activeness.
Contexts.isActive — Method
isActive(contextRule::T) where {T <: AbstractContextRule}Evaluates whether a context rule is active. Supports And, Or, and Not context rules.
Arguments:
contextRule: An AbstractContextRule (And, Or, Not)
Returns true if the rule is active, false otherwise.
Example: isActive(C1 & C2)
isActive(C1 & (C2 | !C3)) # returns true or false