Generics: Parameters, Wrappers and template-specialisation
Castle supports Generics (a bit like Templates in C++) but with a twist. For example, in (the improved version of)
“The Sieve (basic variant)” we use the SlowStart
(base)protocol as a Generic protocol.
It becomes generic as we pass an Argument
to the base class. Only that makes SlowStart
a generic!
It is not visual in the definition.
As this differs from other languages, it gives some questions. We will explain how to use it. And make some (high-level) hints on the implementation.
SlowStart & Sieve
Warning
In this example, one is a Protocol
, and the other is a Component
. Nonetheless, that is an irrelevant detail –
the same applies to other constructs (that support Generics – most do).
The definitions
As “being a generic” isn’t visual in the definition, let’s compare two definitions in The Sieve (basic variant): The Slowstart protocol and the Sieve component.
Both have a (one) formal argument (aka a parameter) in their definition. Without a hint that one is being used as a Generic –although a (at least one )parameter is essential. The list of TypedParameters just gives the option to pass a value(s); here an int in both cases. – like we can in most languages when we instantiate a class.
protocol SlowStart(queue_max:int): EventProtocol {
setMax(queue_len:int);
}
component Sieve(onPrime:int) : Component {
port SimpleSieve<in>:try;
port SimpleSieve<out>:coprime;
}
Instantiate vs Specialise
Nevertheless when using the SlowStart protocol –here when defining the SimpleSieve protocol– it becomes clear that
SlowStart is (used as) a Generic. A Parameter
(here: ‘1’) is bound to a parameter during the definition of
SimpleSieve.
protocol SimpleSieve : SlowStart(1) {
input(int:try);
}
By passing a (one or more) parameter(s) when “subclassing”, we kind of specialise the Generic into a “wrapper
class” in between the (real) base class and the derived class. This in-between protocol (often denoted as Base(1)
or
Base_1
) is not instantiated –it still acts as a “class”. And so, can be subclassed.
It acts exactly as the (real) base class, except that the bound parameter has become build-in – kind of what you expect.
The Sieve Component is used completely dissimilar (or: more traditionally). It is instantiated somewhere in executable code, as we see below.
SimpleSieve.input(newPrime) on self.finder.found
{
alias s;
// Extent the sieve list ...
s:= Sieve.new(newPrime); // See caution, below
...
Caution
The code above uses the code-snipped: s:= Sieve.new(newPrime)
, with the new() method. That syntax detail is fully stable yet.
It could be that it becomes s:= Sieve(newPrime)
(as in Python), or …
The difference
The difference is clear:
When the protocol/component is “new’d”, or “called”, we instantiate.
And we get an element (aka a class instance).When we bound a parameter during a definition, we specialise.
The result is like a “class”.
Another view
Instantiate generic
You may wonder, what happens as we instance a Generic. For example:
- strangeDemo(self, ...)
{
...
p = SlowStart(42) // Remember: SlowStart is (also, used as) a Generic.
...
}
This is allowed! Now, p
[1] has become an instanced protocol (aka an object-instance), where queue_max
is initialised to ‘42’.
Partials (aka closure)
Some languages support the functional programming feature “partial(s)” and/or the related “closure” construct. Both can fix some parameters of existing functions to create a new function with fewer parameters. Conceptually, it is a function that calls another function (with more parameters), without writing out that function completely.
The Castle approach to Generics is very similar: One SlowStart parameter is “fixed” (we speak about ‘bound’) such that
the derived SimpleSieve protocol doesn’t need to be set in when it is initialised.
In the SlowStart
//SimpleSieve
example, we could define a SlowStart1
protocol by reusing the general
SlowStart
protocol but hardcoding the initial values to 1. And derived from that SlowStart1
protocol to define
SimpleSieve.
// This is all pseudo-code -- not valid Castle syntax
protocol SlowStart1 (SlowStart);
implement SlowStart1::
- init(self) {
super.init(1)
}
And, we can do the same for SlowStart2
, SlowStart3
, ...42
, etc.
Or, we can automate that. By binding that parameter by specialising the Generic when we need it.
The effect is the same. Only Castle will generate that code for you.
Footnotes
Comments
comments powered by Disqus