[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