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.

By defining a new Context, a new struct <ContextName>ContextType will be created as a subtype of Context:

struct <ContextName>ContextType <: Context end

and 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 Context1
Contexts.@newContextMacro
@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

source

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.@contextMacro
@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 type
  • expr: 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)

source
Functions for multiple contexts

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

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.isActiveMethod
isActive(context::T) where {T <: Context}

Checks if a context is currently active. Returns true if active, false otherwise.

source

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")
end

The macro @activeContext only calls the function if the context is active:

Contexts.@activeContextMacro
@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 type
  • expr: Function or macro call

Example: @activeContext(MyContext, myfunc(x))

source
Context Groups and State machines

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.activateContextWithoutPNFunction
activateContextWithoutPN(context::T) where {T <: Context}

Activates a context without running PetriNet logic. Mainly used for internal. Should be used with caution

source
Contexts.deactivateContextWithoutPNFunction
deactivateContextWithoutPN(context::T) where {T <: Context}

Deactivates a context without running PetriNet logic. Mainly used for internal. Should be used with caution

source
(De)Activating contexts without PN

(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.OrContextRuleType
OrContextRule <: AbstractContextRule

Represents a logical OR between two contexts or context rules.

Fields:

  • c1, c2: Contexts or context rules.

Example: OrContextRule(ctx1, ctx2)

source
Contexts.AndContextRuleType
AndContextRule <: AbstractContextRule

Represents a logical AND between two contexts or context rules.

Fields:

  • c1, c2: Contexts or context rules.

Example: AndContextRule(ctx1, ctx2)

source
Contexts.NotContextRuleType
NotContextRule <: AbstractContextRule

Represents a logical NOT of a context or context rule.

Fields:

  • c: Context or context rule.

Example: NotContextRule(ctx)

source
Base.:|Method
Base.:|(c1::CT1, c2::CT2) where {CT1, CT2 <: Union{AbstractContext}}

Creates an OrContextRule from two contexts or context rules using the | operator.

Arguments:

  • c1, c2: Contexts or context rules

Returns an OrContextRule.

Example: ctx1 | ctx2

source
Base.:&Method
Base.:&(c1::CT1, c2::CT2) where {CT1, CT2 <: Union{AbstractContext}}

Creates an AndContextRule from two contexts or context rules using the & operator.

Arguments:

  • c1, c2: Contexts or context rules

Returns an AndContextRule.

Example: ctx1 & ctx2

source
Base.:!Method
Base.:!(c::CT) where {CT <: Union{AbstractContext}}

Creates a NotContextRule from a context or context rule using the ! operator.

Arguments:

  • c: Context or context rule

Returns a NotContextRule.

Example: !ctx

source

Those expressions can be checked for activeness.

Contexts.isActiveMethod
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)

source
isActive(C1 & (C2 | !C3)) # returns true or false