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 demo (start/DRAFT)” 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 supports Generics – most do).
The definitions
As “being a generic” isn’t visual in the definition, let’s compare two definition in The Sieve demo (start/DRAFT). That of the Slowstart protocol, and the Sieve component.
Both are defined with a formal argument (aka a parameter). Although it is needed to have (at least) a parameter, this does not reveal one is (used as) a Generic. A list of (one of more) TypedParameters just give 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
) in 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 like:
- 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 have the ability to fix some parameters of existing functions to create a new function with less parameters. Conceptually, it is a function that calles another function (with more parameters), without writing-out that function completely.
The Castle approach to Generics is very simular: A SlowStart parameters is “fixed” (we speak about ‘bound’) such that
the derived SimpleSieve protocol doesn’t need to 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