<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40"><head><meta http-equiv=Content-Type content="text/html; charset=utf-8"><meta name=Generator content="Microsoft Word 15 (filtered medium)"><style><!--
/* Font Definitions */
@font-face
{font-family:Helvetica;
panose-1:0 0 0 0 0 0 0 0 0 0;}
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0cm;
font-size:11.0pt;
font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
{mso-style-priority:99;
color:blue;
text-decoration:underline;}
p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph
{mso-style-priority:34;
margin-top:0cm;
margin-right:0cm;
margin-bottom:0cm;
margin-left:36.0pt;
font-size:11.0pt;
font-family:"Calibri",sans-serif;}
.MsoChpDefault
{mso-style-type:export-only;
font-size:10.0pt;}
@page WordSection1
{size:612.0pt 792.0pt;
margin:70.85pt 70.85pt 2.0cm 70.85pt;}
div.WordSection1
{page:WordSection1;}
/* List Definitions */
@list l0
{mso-list-id:2145854206;
mso-list-type:hybrid;
mso-list-template-ids:-1682421130 67567639 67567641 67567643 67567631 67567641 67567643 67567631 67567641 67567643;}
@list l0:level1
{mso-level-number-format:alpha-lower;
mso-level-text:"%1\)";
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-18.0pt;}
@list l0:level2
{mso-level-number-format:alpha-lower;
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-18.0pt;}
@list l0:level3
{mso-level-number-format:roman-lower;
mso-level-tab-stop:none;
mso-level-number-position:right;
text-indent:-9.0pt;}
@list l0:level4
{mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-18.0pt;}
@list l0:level5
{mso-level-number-format:alpha-lower;
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-18.0pt;}
@list l0:level6
{mso-level-number-format:roman-lower;
mso-level-tab-stop:none;
mso-level-number-position:right;
text-indent:-9.0pt;}
@list l0:level7
{mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-18.0pt;}
@list l0:level8
{mso-level-number-format:alpha-lower;
mso-level-tab-stop:none;
mso-level-number-position:left;
text-indent:-18.0pt;}
@list l0:level9
{mso-level-number-format:roman-lower;
mso-level-tab-stop:none;
mso-level-number-position:right;
text-indent:-9.0pt;}
ol
{margin-bottom:0cm;}
ul
{margin-bottom:0cm;}
--></style></head><body lang=DE-CH link=blue vlink=purple style='word-wrap:break-word'><div class=WordSection1><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>Chuck<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>If I understand you correctly you want to add a “mixin” construct to Oberon.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>Without modifying Oberon-07 you could do it like this:<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>I differentiate three module types:<o:p></o:p></span></p><ol style='margin-top:0cm' start=1 type=a><li class=MsoListParagraph style='margin-left:0cm;mso-list:l0 level1 lfo1'><span lang=EN-US style='mso-fareast-language:EN-US'>The interface definition module<o:p></o:p></span></li><li class=MsoListParagraph style='margin-left:0cm;mso-list:l0 level1 lfo1'><span lang=EN-US style='mso-fareast-language:EN-US'>The interface implementation module(s)<o:p></o:p></span></li><li class=MsoListParagraph style='margin-left:0cm;mso-list:l0 level1 lfo1'><span lang=EN-US style='mso-fareast-language:EN-US'>The interface (or mixin) usage object<o:p></o:p></span></li></ol><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>a)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>The interface definition module groups all methods (and data fields) you define for your interface in a RECORD of procedure variables.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>MODULE PrinterInterface;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>TYPE<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> Obj* = POINTER TO ObjDesc;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> ObjDesc* = RECORD<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> asString*: PROCEDURE (this: Obj; VAR s: ARRAY OF CHAR);<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> val*: INTEGER<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> END<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>END PrinterInterface.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>b)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>The interface implementation module(s) import the interface definition, implement the functionality AND assigns its implementation to the procedure variables in a procedure called New(). Here an example of the first implementation of the PrinterInterface. Other implementation following the same scheme could be called Printer2, Printer3 and so on.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>MODULE Printer1;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>IMPORT PrinterInterface;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>TYPE Obj* = POINTER TO ObjDesc;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> ObjDesc* = RECORD (PrinterInterface.ObjDesc)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> (* local stuff for this interface implementation *)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> END;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>PROCEDURE P2S(this: Obj; VAR s: ARRAY OF CHAR); BEGIN s:=”printer1_x”; s[9] := CHR(this.val MOD 10 + ORD(“0”)) END P2S;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>PROCEDURE New*(VAR o: Obj); BEGIN NEW(o); o.asString := P2S END New;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>END Printer1.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>c)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>Now you can add this interface (aka bunch of procedures to be reused) to any object without inheritance by adding a new RECORD field; adding a new field to the record circumvents the problem of ambiguity if two different interfaces define methods with the same name.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>Hint: For better readability it is best all users of the interface name this record field identical.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>MODULE Usage;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>IMPORT P := Printer1; (*or any other implementation of the PrinterInterface*)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>TYPE<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> User = RECORD<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> name: ARRAY 31 OF CHAR;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> age: INTEGER;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> p: P.Obj (* here you add the “mixin”, no inheritance *)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> END;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>VAR u: User;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>BEGIN<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> NEW(u); P.New(u.p); (* initialize the object and the interface *)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> u.p.asString(u.p, u.name) (* call the interface. The syntax “u.asString” possibly generates ambiguity *)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>END Usage.<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'><o:p> </o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>br<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>Jörg<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'><o:p> </o:p></span></p><div style='border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0cm 0cm 0cm'><p class=MsoNormal><b><span style='font-size:12.0pt;color:black'>Von: </span></b><span style='font-size:12.0pt;color:black'>Oberon <oberon-bounces@lists.inf.ethz.ch> im Auftrag von Charles Perkins <chuck@kuracali.com><br><b>Antworten an: </b>ETH Oberon and related systems <oberon@lists.inf.ethz.ch><br><b>Datum: </b>Sonntag, 25. Oktober 2020 um 17:16<br><b>An: </b>ETH Oberon and related systems <oberon@lists.inf.ethz.ch><br><b>Betreff: </b>Re: [Oberon] Protocols (interfaces) in Oberon-2<o:p></o:p></span></p></div><div><p class=MsoNormal><o:p> </o:p></p></div><div><p class=MsoNormal>Hi <span style='font-family:Helvetica;color:#202124;letter-spacing:.15pt'>Jörg,</span><o:p></o:p></p><div><p class=MsoNormal><b><span style='color:#202124;letter-spacing:.15pt'><br><br></span></b><o:p></o:p></p></div><div><p class=MsoNormal><o:p> </o:p></p></div><div><p class=MsoNormal><span style='font-size:10.5pt;color:#202124;letter-spacing:.15pt'>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.</span><o:p></o:p></p></div><div><p class=MsoNormal><o:p> </o:p></p></div><div><div><p class=MsoNormal><span style='font-size:10.5pt;color:#202124;letter-spacing:.15pt'>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.</span><o:p></o:p></p></div></div><div><p class=MsoNormal><o:p> </o:p></p></div><div><p class=MsoNormal><span style='font-size:10.5pt;color:#202124;letter-spacing:.15pt'>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.</span><o:p></o:p></p></div><div><p class=MsoNormal><o:p> </o:p></p></div><div><p class=MsoNormal><span style='font-size:10.5pt;color:#202124;letter-spacing:.15pt'>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. </span><o:p></o:p></p></div><div><p class=MsoNormal><o:p> </o:p></p></div><div><p class=MsoNormal><span style='font-size:10.5pt;color:#202124;letter-spacing:.15pt'>My two cents...</span><o:p></o:p></p></div><div><p class=MsoNormal><span style='font-size:10.5pt;color:#202124;letter-spacing:.15pt'>Chuck</span><o:p></o:p></p></div><div><p class=MsoNormal><o:p> </o:p></p></div></div><p class=MsoNormal><o:p> </o:p></p><div><div><p class=MsoNormal>On Sun, Oct 25, 2020 at 8:24 AM Jörg <<a href="mailto:joerg.straube@iaeth.ch">joerg.straube@iaeth.ch</a>> wrote:<o:p></o:p></p></div><blockquote style='border:none;border-left:solid #CCCCCC 1.0pt;padding:0cm 0cm 0cm 6.0pt;margin-left:4.8pt;margin-right:0cm'><div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=DE>Chuck</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=DE> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>In plain Oberon-07, you could solve this as follows: Not the nicest syntax but it seems doable.</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>MODULE Dynamic;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>TYPE</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> Obj* = POINTER TO Desc END</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> Desc* = RECORD</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> toString*: PROCEDURE (o: Obj; VAR a: ARRAY OF CHAR)</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> END</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>END Dynamic.</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>MODULE Int;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>IMPORT Dynamic;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>TYPE</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> Obj* = POINTER TO Desc;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> Desc* = RECORD (Dynamic.Desc)</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> val: INTEGER</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> END;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>PROCDEURE I2S(o: Obj; VAR a: ARRAY OF CHAR); BEGIN a:=”integer” END I2S;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>PROCEDURE New*(VAR o: Obj; i: INTEGER); BEGIN NEW(o); o.toString := I2S; o.val := i END New;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>END Int.</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>MODULE Real;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>IMPORT Dynamic;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>TYPE</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> Obj* = POINTER TO Desc;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> Desc* = RECORD (Dynamic.Desc)</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> val: REAL</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> END;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>PROCDEURE R2S(o: Obj; VAR a: ARRAY OF CHAR); BEGIN a:=”real” END R2S;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>PROCEDURE New*(VAR o: Obj; r: REAL); BEGIN NEW(o); o.toString := R2S; o.val := r END New;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>END Real.</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>MODULE Test;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>IMPORT Dynamic, Int, Real, Out;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>VAR i: Int.Obj; r: Real.Obj; dyn: Dynamic.Obj;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>BEGIN</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> Int.New(i, 3); dyn := i;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> dyn.asString(dyn, a); Out.String(a); Out.Ln;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> Real.New(r, 3.14); dyn := r;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> dyn.asString(dyn, a); Out.String(a); Out.Ln;</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US>END Test.</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span lang=EN-US> </span><o:p></o:p></p><div style='border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0cm 0cm 0cm'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><b><span style='font-size:12.0pt;color:black'>Von: </span></b><span style='font-size:12.0pt;color:black'>Oberon <<a href="mailto:oberon-bounces@lists.inf.ethz.ch" target="_blank">oberon-bounces@lists.inf.ethz.ch</a>> im Auftrag von Charles Perkins <<a href="mailto:chuck@kuracali.com" target="_blank">chuck@kuracali.com</a>><br><b>Antworten an: </b>ETH Oberon and related systems <<a href="mailto:oberon@lists.inf.ethz.ch" target="_blank">oberon@lists.inf.ethz.ch</a>><br><b>Datum: </b>Sonntag, 25. Oktober 2020 um 16:03<br><b>An: </b>ETH Oberon and related systems <<a href="mailto:oberon@lists.inf.ethz.ch" target="_blank">oberon@lists.inf.ethz.ch</a>><br><b>Betreff: </b>Re: [Oberon] Protocols (interfaces) in Oberon-2</span><o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Apologies for this : " <span style='font-family:"Courier New"'>x0, x1, u: REAL;"</span><span style='font-family:"Arial",sans-serif'> 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.</span><o:p></o:p></p></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>On Sun, Oct 25, 2020 at 7:33 AM Charles Perkins <<a href="mailto:chuck@kuracali.com" target="_blank">chuck@kuracali.com</a>> wrote:<o:p></o:p></p></div><blockquote style='border:none;border-left:solid #CCCCCC 1.0pt;padding:0cm 0cm 0cm 6.0pt;margin-left:4.8pt;margin-top:5.0pt;margin-right:0cm;margin-bottom:5.0pt'><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>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: <o:p></o:p></p><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><br><span style='font-family:"Courier New"'> VAR W: Texts.Writer;</span><o:p></o:p></p><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;margin-bottom:12.0pt'><span style='font-family:"Courier New"'> TYPE <br> I* = POINTER TO IDesc;<br> IDesc* = RECORD<br> h: INTEGER<br> END ;<br><br> R* = POINTER TO RDesc;<br> RDesc* = RECORD<br> h: REAL<br> END ;<br><br> Stringer* = INTERFACE OF<br> PROCEDURE String* (VAR a: ARRAY OF CHAR) ; <br> END ;<br><br><br> PROCEDURE ( i : I ) String* (VAR a: ARRAY OF CHAR) ;<br> BEGIN a := "integer"<br> END String;<br><br> PROCEDURE ( r : R ) String* (VAR a: ARRAY OF CHAR) ;<br> BEGIN a := "real"<br> END String;</span><o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>In the above scheme an Interface looks just like a collection of type-bound procedure definitions with no bodies.<o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>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. <o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>In Oberon that table-making routine could be satisfied by adding another Trap condition in Kernel.Trap much like how New is implemented.<o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-family:"Courier New"'> PROCEDURE Test*;<br> VAR i: I; r: R; t: ARRAY 32 OF CHAR; <br> s,s2: Stringer; <br> x0, x1, u: REAL;<br> <br> BEGIN <br> NEW(i); NEW(r);</span><o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-family:"Courier New"'> i.h := 3;<br> r.h := 7.5;<br> s := i;</span><o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-family:"Courier New"'> s.Stringer(t); </span><o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-family:"Courier New"'> Texts.WriteString(W,t);<br> s := r;</span><o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-family:"Courier New"'> s.Stringer(t);<br> Texts.WriteString(W,s);</span><o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-family:"Courier New"'> END Test;<br></span><br>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.<o:p></o:p></p></div></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Chuck<o:p></o:p></p></div></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>On Sun, Oct 25, 2020 at 6:27 AM Luca Boasso <<a href="mailto:luke.boasso@gmail.com" target="_blank">luke.boasso@gmail.com</a>> wrote:<o:p></o:p></p></div><blockquote style='border:none;border-left:solid #CCCCCC 1.0pt;padding:0cm 0cm 0cm 6.0pt;margin-left:4.8pt;margin-top:5.0pt;margin-right:0cm;margin-bottom:5.0pt'><div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>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.<o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Do you support something like the following?<o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:13.5pt;font-family:"Courier New"'>TextDesc = RECORD (TextProtocol.TextDesc, WriteProtocol.WriterDesc) END ; (*this means: “implements TextProtocol.TextDesc AND WriteProtocol.WriterDesc "*)</span><o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>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 <a href="https://research.swtch.com/interfaces" target="_blank">https://research.swtch.com/interfaces</a> for one way of doing this, or<a href="http://www.academia.edu/download/42084165/Efficient_Implementation_of_Java_Interfa20160204-28309-28q4h3.pdf" target="_blank"> "Efficient implementation of Java interfaces: Invokeinterface considered harmless" </a><o:p></o:p></p></div></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>On Sun, Oct 25, 2020 at 6:46 AM Andreas Pirklbauer <<a href="mailto:andreas_pirklbauer@yahoo.com" target="_blank">andreas_pirklbauer@yahoo.com</a>> wrote:<o:p></o:p></p></div><blockquote style='border:none;border-left:solid #CCCCCC 1.0pt;padding:0cm 0cm 0cm 6.0pt;margin-left:4.8pt;margin-top:5.0pt;margin-right:0cm;margin-bottom:5.0pt'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Correction: In Text1, it’s TextDesc = RECORD (TextProtocol.TextDesc) of course<br><br>—————————<br><br>Protocols (sometimes called interfaces) can be added to<br>Oberon-2 without adding any keywords to the language.<br><br>This is one of the key differences to how it is usually defined<br>and implemented, e.g. in Swift [*] or in Integrated Oberon [**]<br><br>Under the new minimalistic design, what distinguishes a protocol<br>from an actual implementation (of the class) is that in the protocol<br>definition the implementations of the class methods are simply not<br>defined. Instead, any module that *imports* a protocol definition<br>can “adopt” (i.e. implement) it. See the example below.<br><br>An experimental implementation showed that if the language<br>is extended in *this* way, the implementation cost is minimal.<br><br>But the question is: Is it worth it? Simplicity of implementation<br>should of course not be a criteria for adopting a new feature.<br><br>Personally, I am rather sceptical of the usefulness of protocols.<br>But perhaps someone provides a good reason to adopt them.<br><br>-ap<br><br><br>Example:<br><br> MODULE TextProtocol; (*protocol definition*)<br> TYPE Text = POINTER TO TextDesc;<br> TextDesc = RECORD data*: (*text data*) END ;<br> PROCEDURE (t: Text) Insert (string: ARRAY OF CHAR; pos: LONGINT);<br> PROCEDURE (t: Text) Delete (from, to: LONGINT);<br> PROCEDURE (t: Text) Length (): LONGINT;<br> END TextProtocol;<br><br> MODULE Text1; (*one implementation of the Text protocol*)<br> IMPORT TextProtocol;<br> TYPE Text = POINTER TO TextDesc;<br> TextDesc = RECORD (TextProtocol.TextDesc) END ; (*this means: “implements TextProtocol.TextDesc"*)<br><br> PROCEDURE (t: Text) Insert (string: ARRAY OF CHAR; pos: LONGINT);<br> BEGIN (*implementation of Insert*)<br> END Insert;<br><br> PROCEDURE (t: Text) Delete (from, to: LONGINT);<br> BEGIN (*implementation of Delete*)<br> END Delete;<br><br> PROCEDURE (t: Text) Length (): LONGINT;<br> BEGIN (*implementation of Length*)<br> END Insert;<br> END Text1;<br><br><br> MODULE Text2; (*another implementation of the Text protocol*)<br> IMPORT TextProtocol;<br> TYPE Text = POINTER TO TextDesc;<br> TextDesc = RECORD (TextProtocol.TextDesc) END ; (*this means: “implements TextProtocol.TextDesc"*)<br><br> PROCEDURE (t: Text) Insert (string: ARRAY OF CHAR; pos: LONGINT);<br> BEGIN (*implementation of Insert*)<br> END Insert;<br><br> PROCEDURE (t: Text) Delete (from, to: LONGINT);<br> BEGIN (*implementation of Delete*)<br> END Delete;<br><br> PROCEDURE (t: Text) Length (): LONGINT;<br> BEGIN (*implementation of Length*)<br> END Insert;<br> END Text2;<br><br><br>[*] <a href="https://docs.swift.org/swift-book/LanguageGuide/Protocols.html" target="_blank">https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#</a><br>[**] <a href="https://github.com/io-core/technotes/blob/main/technote014.md" target="_blank">https://github.com/io-core/technotes/blob/main/technote014.md</a><br><br>--<br><a href="mailto:Oberon@lists.inf.ethz.ch" target="_blank">Oberon@lists.inf.ethz.ch</a> mailing list for ETH Oberon and related systems<br><a href="https://lists.inf.ethz.ch/mailman/listinfo/oberon" target="_blank">https://lists.inf.ethz.ch/mailman/listinfo/oberon</a><o:p></o:p></p></blockquote></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>--<br><a href="mailto:Oberon@lists.inf.ethz.ch" target="_blank">Oberon@lists.inf.ethz.ch</a> mailing list for ETH Oberon and related systems<br><a href="https://lists.inf.ethz.ch/mailman/listinfo/oberon" target="_blank">https://lists.inf.ethz.ch/mailman/listinfo/oberon</a><o:p></o:p></p></blockquote></div></blockquote></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>-- <a href="mailto:Oberon@lists.inf.ethz.ch" target="_blank">Oberon@lists.inf.ethz.ch</a> mailing list for ETH Oberon and related systems <a href="https://lists.inf.ethz.ch/mailman/listinfo/oberon" target="_blank">https://lists.inf.ethz.ch/mailman/listinfo/oberon</a> <o:p></o:p></p></div></div><p class=MsoNormal>--<br><a href="mailto:Oberon@lists.inf.ethz.ch" target="_blank">Oberon@lists.inf.ethz.ch</a> mailing list for ETH Oberon and related systems<br><a href="https://lists.inf.ethz.ch/mailman/listinfo/oberon" target="_blank">https://lists.inf.ethz.ch/mailman/listinfo/oberon</a><o:p></o:p></p></blockquote></div><p class=MsoNormal>-- Oberon@lists.inf.ethz.ch mailing list for ETH Oberon and related systems https://lists.inf.ethz.ch/mailman/listinfo/oberon <o:p></o:p></p></div></body></html>