Friday 8 March 2013

Dynamic scope pattern

It's became industry standart for the most languages how the scope of variable is determined. Passing as a parameter, if there are many parameters: creation of value classes, if there are many clients (readers) - we can always create global field.

Visibility of scope is becoming predictable by looking into nesting braces in the source code. That makes it easy to read and understand the code, but in some cases isn't enough for automations and doesn't abstract from non functional TODOs like - automated caching or transaction management, while context for the same should be passed across the whole flow as explicite parameter. 


Global variables are workaround for situations when a lot of executional state must be shared between modules/layers or just methods. For example it's easy to call the 

Context.current()

in any place where we need the power of context class. Additionally that practise is making hard the testing of the code itself (I dont think the usage of PowerMock is good idea, when we can avoid usage of global-static variables during the design phase). Global variables are not the good case in multithreading environment, where there are many concurrent executions, each has separate, independent context.

Dynamic scope is opposite to static - it resolves the variables based on nesting block of code in runtime. In general we are loosing the incapsulation and getting harder to determine visually dependencies, but in Scala dynamic scope practices are improved comparing to Java, and it's good idea to mix dynamic scope pattern with others patterns like Loan or Cake.

Let's take a look closer to practices closer.

1. Inner Class/Function

This one is the most easy to get, but isn't flexible enough for all cases.

For example:
Inside the state object GlobalScope we are manipulating in UserFactory class, which refers to context by fields of outer class. Actually this approach stands on border between static and dynamic scope patterns, while we have the advantage of static checking in meantime UserFactory is abstracted from context creation via dynamic resolving the state. Each time we need the different State - we must initialize the new GlobalScope instance, that limits us in the possible set of different combinations. Can be used with number 4 - Cake Pattern That can improve code readability, improve testing tactics and make more state combinations to build in runtime.

2. Thread Local value.

ThreadLocal class became one of the favourite inside JEE IoC containers. It allows to bind the state to current thread,  that shines for web frameworks, when one Thread dispatches the one request - response  session, and can be used as reliable storage unit for execution context. While it's hidden behind IoC frameworks it's easy to use, but when we need need global variables holding the values for each thread separately - it's good practice to mix Thread Local method with loan pattern. This will improve code readability and prove the correct usage of nested code readability. Scala already contains implementations of the both patterns in one class DynamicVariable. In fact it's using Java's InheritableThreadLocal class for containing the thread dependent value:
We can now do some computations, where the enclosing scope is limited by loan pattern closure, and we are proven that each thread will have own execution context.


3. Implicit parameter

I've already described the implicit parameters in therms of View and Context Bound, but the simplest usage of implicit parameter is Dynamic Scope implementation. Scala resolves the implicit params by Type, the name or attributes etc are ignored, that limits the features comparing to IoC Frameworks. As I mentioned in Loan Pattern the best one example of mixing the Loan and Implicit Parameter is STM example:
Again with help of Loan we are limiting the scope of implicit parameter application borders (closure). As we can find static checking is applicable to implicit parameter, but it's no much better than just passing as a parameter, because implicit value declaration must be present on all level of call stack, that is statically determined.
Scala allows to declare implicit values in some place, that can be imported later, to make it applicable to all visible scope, but breaks the incapsulation and adds additional level of risks to the design.
For example:

    import com.myframework.DefaultImplicits._

4. Cake pattern.

As a Inner Class or Function the Cake Paatern standing between static and dynamic scoping. 
Reworking the first example in Cake style, we can request the presents of something (in out case GlobalScope) in scope execution:
To make it dynamic scope driven, we can mix the different traits, to receive different enclosing states, but the set is limited by number of implementations of GlobalScope trait:

No comments:

Post a Comment