this
and interfaces ¶More work in the ‘typed-language’ branch of Vicare, for both the expander and the built–in types infrastructure.
Everything I discuss here is relative to code in the head of the ‘typed-language’ branch.
this
¶Under many programming languages that support some form of object–oriented
programming, object–types have methods; in the definition of methods the first
argument is the type instance itself. Usually such argument is implicit (like under
C++) and it can be accessed through the reserved keyword this
.
Until recently, this was not the case under Vicare, where method
clauses
in record–type definitions required us to explicitly write the first argument:
(define-record-type <duo> (fields one two) (method (add {O <duo>}) (+ (.one O) (.two O)))) (define O (new <duo> 1 2)) (.add O) ⇒ 3
This is no more. (vicare)
now exports the fluid syntax this
, which
is meant to be used to access the implicit first argument of methods. So the code
above is now invalid and it must be written:
(define-record-type <duo> (fields one two) (method (add) (+ (.one this) (.two this)))) (define O (new <duo> 1 2)) (.add O) ⇒ 3
where this
references a read–only variable of type <duo>
.
Everywhere the clauses method
, case-method
and
method/overload
are used to define methods: the first argument is implicit
and the fluid syntax this
is used in the body of methods to access it.
Interfaces are a mechanism to verify, at expand–time, that: instances of an
object–type can be used in a generic expression, because they implement all the
needed methods and such methods can be called at run–time through dynamic
dispatching. The current implementation is to be considered experimental; some of
the internals are in the boot image and the rest is in the library (vicare
language-extensions interfaces)
.
Let’s consider this code:
(define-record-type <a-vector> (fields {vec <nevector>}) (method ({first <top>}) (vector-ref (.vec this) 0))) (define-record-type <a-string> (fields {vec <nestring>}) (method ({first <top>}) (string-ref (.vec this) 0))) (define-record-type <a-list> (fields {vec <nelist>}) (method ({first <top>}) (car (.vec this)))) (define (fun O) (.first O)) (fun (new <a-vector> '#(1 2 3))) ⇒ 1 (fun (new <a-string> "ABC")) ⇒ #\A (fun (new <a-list> '(a b c))) ⇒ a
everything works fine in the function fun
because all of <a-vector>
,
<a-string>
and <a-list>
implement the method ‘first’. The code
(.first O)
expands into a call to method-call-late-binding
, which, at
run–time, finds the method implementation functions in the type descriptors of
<a-vector>
, <a-string>
and <a-list>
.
Fine, but the code is not type–checked at expand–time. Enter interfaces. Let’s modify the code as follows:
(define-interface <Sequence> (method-prototype first (lambda () => (<top>)))) (define-record-type <a-vector> (implements <Sequence>) (fields {vec <nevector>}) (method ({first <top>}) (vector-ref (.vec this) 0))) (define-record-type <a-string> (implements <Sequence>) (fields {vec <nestring>}) (method ({first <top>}) (string-ref (.vec this) 0))) (define-record-type <a-list> (implements <Sequence>) (fields {vec <nelist>}) (method ({first <top>}) (car (.vec this)))) (define (fun {O <Sequence>}) (.first O)) (fun (new <a-vector> '#(1 2 3))) ⇒ 1 (fun (new <a-string> "ABC")) ⇒ #\A (fun (new <a-list> '(a b c))) ⇒ a
everything works almost as before, but the record–type definition clause
(implements <Sequence>)
causes the expander to validate, at expand-time, that
the record–types actually implement a method first
with the correct type
signature.
Also, the function application (fun ?operand)
is validated at
expand–time to verify that the type of ?operand is an object–type that
implements <Sequence>
. Such validation can happen only if the
expander is able to determine the type of ?operand; this validation cannot
happen at run–time, so, for example, it is impossible for label types to implement
interfaces.
Interfaces can implement methods, case–methods and overloaded methods of their own, with the same syntax used for record–types. Right now an interface type cannot declare another interface as super–type; I will decide what to do in future.