Before we begin and I forget — if you place any extension at the same file the type is defined you have full access to all private members of it. Phew, now we can begin…
The idea of external extensions are taken from C# — I simply love it. The
extension keyword and grouping is taken from Swift.
Those are pure syntactic sugar, there is nothing you couldn’t write by yourself using non-extension part of the language.
Let’s say you have collection of collection of some elements, the common task is to
flatten the data. You cannot add such method to
Sequence type, but external method just fits right in:
extension def flatten<E>(this ~~E) ~E
Three things to notice:
- the function is placed directly in (any) namespace,
- the modifier is
- there is explicit
Such extension can be used as a function or as a method:
_ = flatten([[1,2],[3,4]]);
_ = [[1,2],[3,4]].flatten();
Here we group the methods and properties into extension type — I save better example for later so let’s use absurd one:
extension class Real
def square Real get => this*this;
def cube Real get => square*this;
We didn’t change the source of the type. We just added new properties on the side:
_ = 4.5.square;
_ = 1.7.cube;
Please note in case of extension types, we don’t add explicitly
this parameter and we can only call methods as, well, methods.
I didn’t have time (and a book) to check in detail how much Skila differs from Swift but it seems there was a different need that drove the idea of this extension, thus Skila extensions are more limited. Currently I am reading about Objective-C — and surprise, surprise — I noticed it supports extensions.
Unlike C++ template specializations you cannot provide customized version of a given method present in a template. You can only add new methods (or properties) which do not exist in a template being extended.
Consider two sequences of some elements. Would it be useful to test whether those sequences are equal?
if [a,b] == [c,d] then
It didn’t work, because
Sequence couldn’t blindly assume its elements support equality test. With our new tool we can add appropriate method. Our first try:
extension interface Sequence<T>
where T refines Equatable;
def ==(cmp ~T) Bool
With above external extension we can test equality of two sequences in safe manner (this is not true in C# which provides unsafe
Equals — pretty much the same as in dynamic languages).
But is it really all? No — despite we can perform the test, we didn’t change the
Sequence type so we cannot compare a sequence of a sequences of
Equatable elements, because the sequence of
Equatable elements does not inherit
%% does not work with above extension
if [[a,b],[i,j]] == [[c,d],[k,l]] then
We need internal extension (specialization):
extension interface Sequence<T> refines Equatable
where T refines Equatable;
refines def ==(cmp ~T) Bool
What does it mean? A general
Sequence type does not inherit from
Equatable interface (because it cannot), but once we drop something that is
Sequence type, we get type specialization which does inherit from
There is more — a type which inherits from
!Array) also behaves in the same way, its general version is not
Equatable, but if you have let’s say array of numbers (which are
Equatable) entire array becomes
Equatable — think of it as one specialization inherits from another specialization.
Specializations can be combined — imaginary example, if we wrote specialization for
Sequence to inherit from
X when elements inherit from
X, and in same manner we did with type
Y what happens if we have
Sequence of elements which derive from
Y at the same time? We will get
Sequence type inheriting from
The bill? You have to place internal extensions in the same file as type being extended. Also you cannot extend non-template type (it does not make sense in Skila perspective, because there would be no way to discriminate such extension).