This is the first article in a series looking at the Inversion of Coupling Control type system for composition. The series will demonstrate how the resulting type system allows for easy composition. This avoids much of the complexity of Functional / Object-Oriented composition. The resulting type system and it’s resulting composition is simple enough even for junior developers to comprehend.
This article will look at the type system and composition of First-Class Procedures.
We’ve previously talked about the OO Matrix and how the method (and for that matter the function) suffers from coupling. We’ve also discussed how this coupling can be reduced by Inversion of Coupling Control. We’ve also shown how these inverted methods/functions can be weaved seamlessly together. We’ve even seen the industry moving towards these concepts with microservices.
What we haven’t yet discussed is the type system and composition available with First-Class Procedures.
To discuss this, let’s start with the well known concept of Dependency Injection.
Meta-data of dependency injection describes a list of dependencies with the following three attributes:
- Field (or constructor parameter) to inject the dependency (providing the dependency’s name – e.g. field name)
- Type of the dependency
- Optional qualifier (distinguishes dependencies of the same type)
With this information, the matching to dependencies can be undertaken. Note, some dependency injection frameworks have improvements on this. However, the above meta-data is typically adequate for auto-wiring dependencies of the object.
Continuation Injection reduces a function/method invocation to a similarly simple set of attributes as Dependency Injection.
As only a single parameter can be passed (remaining are dependency injected), there is only one type required. This is the optional parameter type.
Also, the continuations from the First-Class Procedure are all named.
Note that qualifying continuations is deemed error prone. Automatically wiring continuation to matching First-Class Procedures requires differing parameter types for each continuation in the system. In practice, most of the time it is the same simple domain data types being passed around. So you end up having to qualify every continuation.
Furthermore, qualifying continuations provides little improvement on reducing complexity. In qualifying all continuations you end up writing configurations for each continuation. In other words, you end up providing a mapping of each continuation name to servicing First-Class Procedure. This configuration is very error prone. We will show how this is made less error prone and easily comprehensible shortly.
Therefore, each required continuation by the First-Class Procedure is described with the following meta-data:
- Name of the continuation
- Type of the parameter for the continuation (i.e. type of argument sent to continuation)
Note that exceptions from the method/function are also modeled as continuations. The type is the exception type. The name is the name of the exception type.
First-Class Procedure Type System
Putting the Continuation Injection meta-data together with the Dependency Injection meta-data, we get the following meta-data (type) information for the First-Class Procedure:
The above type can describe all First-Class Procedures. As a method/function requires parameters, these are described by the DependencyType listing. As method/functions require calling other method/functions, this is described by the ContinuationType listing. As there is only one parameter to the First-Class Procedure, the parameter type describes the type of argument that must be supplied by invoking ContinuationType.
Note that we have not discussed Thread Injection typing. This is actually derived form the DependencyType listing by matching on object’s type to thread pool. See Thread Injection for more information.
As we now can type the First-Class Procedure, we can look at using this type system for composition.
First-Class Procedure Composition
The composition of First-Class Procedures is focused on wiring together the continuations. Dependency Injection provides the objects (state) to the methods/functions. This wiring of dependencies is done separate to the continuations. Therefore, we need only focus on continuations being wired to their servicing First-Class Procedures.
Now it is possible to do this in code and even configuration files. However, as the system grows in complexity this listing of continuation name to servicing First-Class Procedures becomes very unwieldy. Trying to decipher the system behaviour from lists of linkages becomes very difficult.
Therefore, we look at the First-Class Procedure as the following:
- a processing node with an input anchor
- varying number of output anchors for each of it’s continuations
- linkages as lines from output anchor to input anchor
This representation makes for graphical configuration of the composition. The First-Class Procedure is nodes in the graph with lines representing the continuations between them. Therefore, composition of First-Class Procedures is quite literally drawing lines between them.
As the graphical configuration is visually easy to comprehend, it makes it very easy for even junior developers to understand the application.
We have demonstrated how Continuation Injection simplifies invocation of methods/functions. This simple meta-data model for Continuation Injection is similar to Dependency Injection for objects.
This simple meta-data enables a type system for First-Class Procedures. This type system enables graphical composition of First-Class Procedures.
Furthermore, being graphical, the composition is very easy for junior developers to comprehend.
The next article will discuss how the First-Class Procedure type system is actually made more general to enable modularising of the application. However, these modules still maintain a similar type system for inclusion in the graphical configuration.
Opinions expressed by Java Code Geeks contributors are their own.