16.1 Classes
In many languages we can organise groups of definitions into some kind of namespace. In C++ we can use namespaces or classes.
16.1.1 Basic class and reference
In Felix, our namespace construction is called class
.
Here is an example:
class A { fun f: int->int; fun g: int->int; }
To use a symbol from a class we can use qualification with a similar syntax to C++:
var x1 = A::f (1); var x2 = A::g (1);
16.1.2 Nested Classes
Classes can be nested:
class B { fun h: int -> int; class C { fun k: int -> int; } } fun glo: int -> int;
and of course the obvious syntax is used for qualified names:
var x3 = B::C::k (1); var x3a = root::glo (1);
Note the outermost scope is designated by the special name root
.
The usual C++ syntax of an initial {::} is not permitted.
16.1.3 Derived classes
Given a class you can make another which inherits the symbols from the first. The class from which we inherit is termed a base for the one doing the inheriting which is said to be derived:
class Base { fun f: int -> int; } class Derived { inherit Base; fun g: int-> int; } var x4 = Base::f (1); var x5 = Derived::g (1); var x6 = Derived:: f(1);
It is important to understand that the injection of the symbol f
defined in Base
into Derived
allows lookup to find f
in
Derived
with qualified lookup syntax {Derived::f} but it does
not make the function entity a member of Derived
.
Instead you should think of inheritance creating a pointer to the original symbol. I will show why you must take this view shortly.
16.1.4 Opening classes
You can avoid qualified access by opening a class.
This can be done with the open
directive:
open Base; open Derived; var x7 = f(1); var x8 = g(1);
Now, you may wonder whether that f
is the one from
class Base
of the one from class Derived
. If you are wondering
you didn't read the previous comment!
There are two access paths to the function f
, namely {Base::f}
and {Derived::f}, but there's only one function. Opening the two
classes makes both paths implicit so there are two ways to find f
.
But there is still only one f
so there is no ambiguity applying it.
If you open a class inside another, the additional search paths are only available inside the class and any nested scopes. Such symbols are private to the class. For example:
class Another { open Base; fun k(x:int)=>f(x); } // Another::f does not work!
16.1.5 Hiding exposed symbols
A symbol exposed through an open
directive
will be hidden by a definition or injection from
an inherit directive of the same name (for non-functions)
or same name and signature (for functions).
Intuitively, opened scopes hide just behind the current scope, so in a nested context we have a list of search scopes: the inner most scope, then symbols exposed by opens of that scope, then the next innermost scope, then its opens, up to the top level scope.
class X { open Base; fun f: int -> int; fun g(x:int) => f x; // X::f }