[Oberon] Protocols (interfaces) in Oberon-2

Charles Perkins chuck at kuracali.com
Sun Oct 25 18:57:13 CET 2020


ooh, more things to read!

On Sun, Oct 25, 2020 at 10:54 AM Luca Boasso <luke.boasso at gmail.com> wrote:

> I agree with Chuck, interfaces are useful to avoid the issues of
> implementation inheritance all together and use composition instead.
> This has been explored by former ETH PhDs with the language Lagoona, see On
> Reconciling Objects, Components,and Efficiency in Programming Languages
> <http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.76.8656&rep=rep1&type=pdf>
> for rationale and implementation techniques (the language Emerald follows a
> similar approach)
>
> On Sun, Oct 25, 2020 at 11:15 AM Charles Perkins <chuck at kuracali.com>
> wrote:
>
>> Hi Jörg,
>>
>>
>> If I understand your example correctly, the Interface procedures are
>> declared in the base "Dynamic" module while their implementation resides in
>> a derivative, potentially separately compiled record definition. This is a
>> useful pattern for a self-contained project. It requires you to make every
>> interface implementation a descendant of one record in the "Dynamic"
>> however. Every time you add a new interface you have to recompile the
>> dynamic module and everything that uses it, because the symbol file has
>> changed. In addition, different developers may be adding interfaces
>> independently. Either everyone is making changes to the same "Dynamic"
>> module and their changes have to be merged, or each developer has a
>> different "Dynamic" (or otherwise-named) module and their interfaces do not
>> compose.
>>
>> I would like to be able to request the same behavior from two completely
>> independent types, and if the interfaces match, perform the function call.
>> The idea I'm going for is something that will allow you to make, for
>> example, an interface that will print anything with a "printer" method, a
>> different interface that will serialize to JSON anything with a "ToJSON"
>> method, etc.
>>
>> Philosophically though I think it's the same question of why have
>> Oberon-2 with type-bound procedures when the same functionality can be
>> achieved with plain Oberon-07 and procedure variables?  Type-bound
>> procedures introduce complexity in the compiler and module formats that are
>> not strictly necessary.
>>
>> Just as Oberon-2 is not the same language as Oberon, I think a language
>> that has this 'dynamic trait' capability should be called something else,
>> like perhaps Oberon-2i.
>>
>> My two cents...
>> Chuck
>>
>>
>> On Sun, Oct 25, 2020 at 8:24 AM Jörg <joerg.straube at iaeth.ch> wrote:
>>
>>> Chuck
>>>
>>>
>>>
>>> In plain Oberon-07, you could solve this as follows: Not the nicest
>>> syntax but it seems doable.
>>>
>>>
>>>
>>> MODULE Dynamic;
>>>
>>> TYPE
>>>
>>>   Obj* = POINTER TO Desc END
>>>
>>>   Desc* = RECORD
>>>
>>>     toString*: PROCEDURE (o: Obj; VAR a: ARRAY OF CHAR)
>>>
>>>   END
>>>
>>> END Dynamic.
>>>
>>>
>>>
>>> MODULE Int;
>>>
>>> IMPORT Dynamic;
>>>
>>> TYPE
>>>
>>>   Obj* = POINTER TO Desc;
>>>
>>>   Desc* = RECORD (Dynamic.Desc)
>>>
>>>     val: INTEGER
>>>
>>>   END;
>>>
>>>
>>>
>>> PROCDEURE I2S(o: Obj; VAR a: ARRAY OF CHAR); BEGIN a:=”integer” END I2S;
>>>
>>> PROCEDURE New*(VAR o: Obj; i: INTEGER); BEGIN NEW(o); o.toString := I2S;
>>> o.val := i END New;
>>>
>>>
>>>
>>> END Int.
>>>
>>>
>>>
>>> MODULE Real;
>>>
>>> IMPORT Dynamic;
>>>
>>> TYPE
>>>
>>>   Obj* = POINTER TO Desc;
>>>
>>>   Desc* = RECORD (Dynamic.Desc)
>>>
>>>     val: REAL
>>>
>>>   END;
>>>
>>>
>>>
>>> PROCDEURE R2S(o: Obj; VAR a: ARRAY OF CHAR); BEGIN a:=”real” END R2S;
>>>
>>> PROCEDURE New*(VAR o: Obj; r: REAL); BEGIN NEW(o); o.toString := R2S;
>>> o.val := r END New;
>>>
>>>
>>>
>>> END Real.
>>>
>>>
>>>
>>> MODULE Test;
>>>
>>> IMPORT Dynamic, Int, Real, Out;
>>>
>>> VAR i: Int.Obj; r: Real.Obj; dyn: Dynamic.Obj;
>>>
>>> BEGIN
>>>
>>>   Int.New(i, 3); dyn := i;
>>>
>>>  dyn.asString(dyn, a); Out.String(a); Out.Ln;
>>>
>>>   Real.New(r, 3.14); dyn := r;
>>>
>>>   dyn.asString(dyn, a); Out.String(a); Out.Ln;
>>>
>>> END Test.
>>>
>>>
>>>
>>>
>>>
>>> *Von: *Oberon <oberon-bounces at lists.inf.ethz.ch> im Auftrag von Charles
>>> Perkins <chuck at kuracali.com>
>>> *Antworten an: *ETH Oberon and related systems <oberon at lists.inf.ethz.ch
>>> >
>>> *Datum: *Sonntag, 25. Oktober 2020 um 16:03
>>> *An: *ETH Oberon and related systems <oberon at lists.inf.ethz.ch>
>>> *Betreff: *Re: [Oberon] Protocols (interfaces) in Oberon-2
>>>
>>>
>>>
>>> Apologies for this : " x0, x1, u: REAL;" was a fragment that snuck in
>>> from testing. I have the above code successfully parsing in a fork of
>>> Andreas's Extended Oberon compiler. I don't have the method table
>>> generation code working yet. It turns out that the Run-time needs to have
>>> symbolic type information available, which could be simply loading the smb
>>> file alongside the rsc file for code that uses interfaces, or it could
>>> involve embedding a hash of the name and parameters of the type-bound
>>> procedure in another section of the rsc file. I haven't decided yet.
>>>
>>>
>>>
>>> On Sun, Oct 25, 2020 at 7:33 AM Charles Perkins <chuck at kuracali.com>
>>> wrote:
>>>
>>> I think Interfaces / Protocols / Dynamic Traits (what Rust calls them)
>>> would be a quite useful extension to Oberon. I'm looking at doing it a
>>> different way, like this:
>>>
>>>
>>>   VAR W: Texts.Writer;
>>>
>>>
>>>
>>>   TYPE
>>>        I* = POINTER TO IDesc;
>>>        IDesc* = RECORD
>>>             h: INTEGER
>>>        END ;
>>>
>>>        R* = POINTER TO RDesc;
>>>        RDesc* = RECORD
>>>             h: REAL
>>>        END ;
>>>
>>>        Stringer* = INTERFACE OF
>>>             PROCEDURE String* (VAR a: ARRAY OF CHAR) ;
>>>        END ;
>>>
>>>
>>>   PROCEDURE ( i : I ) String* (VAR a: ARRAY OF CHAR) ;
>>>   BEGIN a := "integer"
>>>   END String;
>>>
>>>   PROCEDURE ( r : R ) String* (VAR a: ARRAY OF CHAR) ;
>>>   BEGIN a := "real"
>>>   END String;
>>>
>>> In the above scheme an Interface looks just like a collection of
>>> type-bound procedure definitions with no bodies.
>>>
>>>
>>>
>>> The trick is when it comes time to use the interface, which is when the
>>> code needs to know which actual procedure to call based on the record type
>>> assigned to it during execution. The record type assigned to an interface
>>> could be any record that contains the String type-bound procedure (in this
>>> case.) It might be the first method, or the third, or the sixth... Go
>>> solves this by generating a dispatch table for the Interface when a type is
>>> assigned to it.
>>>
>>>
>>>
>>> In Oberon that table-making routine could be satisfied by adding another
>>> Trap condition in Kernel.Trap much like how New is implemented.
>>>
>>>
>>>
>>>   PROCEDURE Test*;
>>>       VAR i: I; r: R; t: ARRAY 32 OF CHAR;
>>>         s,s2: Stringer;
>>>         x0, x1, u: REAL;
>>>
>>>   BEGIN
>>>       NEW(i); NEW(r);
>>>
>>>       i.h := 3;
>>>       r.h := 7.5;
>>>       s := i;
>>>
>>>       s.Stringer(t);
>>>
>>>       Texts.WriteString(W,t);
>>>       s := r;
>>>
>>>       s.Stringer(t);
>>>       Texts.WriteString(W,s);
>>>
>>>    END Test;
>>>
>>> The above idea for Interfaces builds on the mechanisms already in place
>>> in the Oberon-2 compiler and run-time. I think it would be quite useful for
>>> allowing a program to choose from multiple implementations of an interface
>>> without constraining them to derive from the same base type while still
>>> keeping strong static typing and separate linking and loading.
>>>
>>>
>>>
>>> Chuck
>>>
>>>
>>>
>>> On Sun, Oct 25, 2020 at 6:27 AM Luca Boasso <luke.boasso at gmail.com>
>>> wrote:
>>>
>>> A key feature of protocols / interfaces is the safe multiple
>>> inheritance: you can explicitly or implicitly (like in the Go language)
>>> implement several interfaces and be type compatible with each one of them.
>>>
>>>
>>>
>>> Do you support something like the following?
>>>
>>>
>>>
>>> TextDesc = RECORD (TextProtocol.TextDesc, WriteProtocol.WriterDesc) END
>>> ;  (*this means: “implements TextProtocol.TextDesc AND
>>> WriteProtocol.WriterDesc "*)
>>>
>>>
>>>
>>> If this is not supported I don't see this feature being that useful. To
>>> support the feature above the implementation is more complicated than
>>> Oberon-2's bound procedures. See https://research.swtch.com/interfaces
>>> for one way of doing this, or "Efficient implementation of Java
>>> interfaces: Invokeinterface considered harmless"
>>> <http://www.academia.edu/download/42084165/Efficient_Implementation_of_Java_Interfa20160204-28309-28q4h3.pdf>
>>>
>>>
>>>
>>> On Sun, Oct 25, 2020 at 6:46 AM Andreas Pirklbauer <
>>> andreas_pirklbauer at yahoo.com> wrote:
>>>
>>> Correction: In Text1, it’s TextDesc = RECORD (TextProtocol.TextDesc) of
>>> course
>>>
>>> —————————
>>>
>>> Protocols (sometimes called interfaces) can be added to
>>> Oberon-2 without adding any keywords to the language.
>>>
>>> This is one of the key differences to how it is usually defined
>>> and implemented, e.g. in Swift [*] or in Integrated Oberon [**]
>>>
>>> Under the new minimalistic design, what distinguishes a protocol
>>> from an actual implementation (of the class) is that in the protocol
>>> definition the implementations of the class methods are simply not
>>> defined. Instead, any module that *imports* a protocol definition
>>> can “adopt” (i.e. implement) it. See the example below.
>>>
>>> An experimental implementation showed that if the language
>>> is extended in *this* way, the implementation cost is minimal.
>>>
>>> But the question is: Is it worth it? Simplicity of implementation
>>> should of course not be a criteria for adopting a new feature.
>>>
>>> Personally, I am rather sceptical of the usefulness of protocols.
>>> But perhaps someone provides a good reason to adopt them.
>>>
>>> -ap
>>>
>>>
>>> Example:
>>>
>>>   MODULE TextProtocol;  (*protocol definition*)
>>>     TYPE Text = POINTER TO TextDesc;
>>>       TextDesc = RECORD data*: (*text data*) END ;
>>>       PROCEDURE (t: Text) Insert (string: ARRAY OF CHAR; pos: LONGINT);
>>>       PROCEDURE (t: Text) Delete (from, to: LONGINT);
>>>       PROCEDURE (t: Text) Length (): LONGINT;
>>>   END TextProtocol;
>>>
>>>   MODULE Text1; (*one implementation of the Text protocol*)
>>>     IMPORT TextProtocol;
>>>     TYPE Text = POINTER TO TextDesc;
>>>       TextDesc = RECORD (TextProtocol.TextDesc) END ;  (*this means:
>>> “implements TextProtocol.TextDesc"*)
>>>
>>>     PROCEDURE (t: Text) Insert (string: ARRAY OF CHAR; pos: LONGINT);
>>>     BEGIN (*implementation of Insert*)
>>>     END Insert;
>>>
>>>     PROCEDURE (t: Text) Delete (from, to: LONGINT);
>>>     BEGIN (*implementation of Delete*)
>>>     END Delete;
>>>
>>>     PROCEDURE (t: Text) Length (): LONGINT;
>>>     BEGIN (*implementation of Length*)
>>>     END Insert;
>>>   END Text1;
>>>
>>>
>>>   MODULE Text2; (*another implementation of the Text protocol*)
>>>     IMPORT TextProtocol;
>>>     TYPE Text = POINTER TO TextDesc;
>>>       TextDesc = RECORD (TextProtocol.TextDesc) END ;  (*this means:
>>> “implements TextProtocol.TextDesc"*)
>>>
>>>     PROCEDURE (t: Text) Insert (string: ARRAY OF CHAR; pos: LONGINT);
>>>     BEGIN (*implementation of Insert*)
>>>     END Insert;
>>>
>>>     PROCEDURE (t: Text) Delete (from, to: LONGINT);
>>>     BEGIN (*implementation of Delete*)
>>>     END Delete;
>>>
>>>     PROCEDURE (t: Text) Length (): LONGINT;
>>>     BEGIN (*implementation of Length*)
>>>     END Insert;
>>>   END Text2;
>>>
>>>
>>> [*] https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#
>>> <https://docs.swift.org/swift-book/LanguageGuide/Protocols.html>
>>> [**] https://github.com/io-core/technotes/blob/main/technote014.md
>>>
>>> --
>>> Oberon at lists.inf.ethz.ch mailing list for ETH Oberon and related systems
>>> https://lists.inf.ethz.ch/mailman/listinfo/oberon
>>>
>>> --
>>> Oberon at lists.inf.ethz.ch mailing list for ETH Oberon and related systems
>>> https://lists.inf.ethz.ch/mailman/listinfo/oberon
>>>
>>> -- Oberon at lists.inf.ethz.ch mailing list for ETH Oberon and related
>>> systems https://lists.inf.ethz.ch/mailman/listinfo/oberon
>>> --
>>> Oberon at lists.inf.ethz.ch mailing list for ETH Oberon and related systems
>>> https://lists.inf.ethz.ch/mailman/listinfo/oberon
>>>
>> --
>> Oberon at lists.inf.ethz.ch mailing list for ETH Oberon and related systems
>> https://lists.inf.ethz.ch/mailman/listinfo/oberon
>>
> --
> Oberon at lists.inf.ethz.ch mailing list for ETH Oberon and related systems
> https://lists.inf.ethz.ch/mailman/listinfo/oberon
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.inf.ethz.ch/pipermail/oberon/attachments/20201025/410bc43f/attachment.html>


More information about the Oberon mailing list