<div dir="ltr">Hmm...<div><br></div><div>Everything that implements an interface must be a descendant of Module Data but Data is empty, therefore its signature never needs to change. The use of an interface is checked in the CASE statement and cast there to the interface needed by the client. I think multiple interfaces can be implemented by successively extending Data. This could do it!</div><div><br></div><div>I am going to try to use this, thank you <span style="color:rgb(32,33,36);font-family:Roboto,RobotoDraft,Helvetica,Arial,sans-serif;font-size:14px;letter-spacing:0.2px;white-space:nowrap">Jörg for your patience with me.</span></div><div><span style="color:rgb(32,33,36);font-family:Roboto,RobotoDraft,Helvetica,Arial,sans-serif;font-size:14px;letter-spacing:0.2px;white-space:nowrap"><br></span></div><div><span style="color:rgb(32,33,36);font-family:Roboto,RobotoDraft,Helvetica,Arial,sans-serif;font-size:14px;letter-spacing:0.2px;white-space:nowrap">Cheers,</span></div><div><span style="color:rgb(32,33,36);font-family:Roboto,RobotoDraft,Helvetica,Arial,sans-serif;font-size:14px;letter-spacing:0.2px;white-space:nowrap">Chuck</span></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Oct 26, 2020 at 11:42 AM Jörg <<a href="mailto:joerg.straube@iaeth.ch">joerg.straube@iaeth.ch</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div lang="DE-CH" style="overflow-wrap: break-word;"><div class="gmail-m_8978136752056602891WordSection1"><p class="MsoNormal"><span lang="EN-US">Chuck<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US"><u></u> <u></u></span></p><p class="MsoNormal"><span lang="EN-US">Okay. My proposal in standard Oberon-07 for your new requirement is this:<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US"><u></u> <u></u></span></p><p class="MsoNormal"><span lang="EN-US">MODULE Data;<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">TYPE<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">  Any* = POINTER TO Empty;<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">  Empty* = RECORD END;<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">END Data.<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US"><u></u> <u></u></span></p><p class="MsoNormal"><span lang="EN-US">MODULE Jsonify;<br>IMPORT Data;<br>TYPE<br>  Methods* = POINTER TO MDesc;<br>  MDesc* = RECORD<br>    toJSON: PROCEDURE(this: Data.Any);<br>  END;<br>END Jsonify.<br><br></span><span lang="EN-US"><u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">MODULE myData;<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">IMPORT Data;<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">TYPE<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">  Tree* = POINTER TO TreeDesc;<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">  TreeDesc* = RECORD (Data.Empty)<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">    left, right: Tree;<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">    j: Jsonify;<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">  END;<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">VAR t: Tree;<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">PROCEDURE J(t: Data.Any);<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">  BEGIN<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">    CASE t OF Tree: (* implement here your Tree-specific JSON routine *) <u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">    END<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">  END J;<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">PROCEDURE New*(VAR t: Tree); BEGIN NEW(t); Jsonify.New(t.j); t.j.toJSON := J END New<br><br><u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">BEGIN<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US"> </span> New(t); t.j.toJSON(t)<u></u><u></u></p><p class="MsoNormal"><span lang="EN-US">END myData.</span><span lang="EN-US"><u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US"><u></u> <u></u></span></p><p class="MsoNormal"><span lang="EN-US">br<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US">Jörg<u></u><u></u></span></p><p class="MsoNormal"><span lang="EN-US"><u></u> <u></u></span></p><div style="border-right:none;border-bottom:none;border-left:none;border-top:1pt solid rgb(181,196,223);padding:3pt 0cm 0cm"><p class="MsoNormal"><b><span style="font-size:12pt;color:black">Von: </span></b><span style="font-size:12pt;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>Montag, 26. Oktober 2020 um 17: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<u></u><u></u></span></p></div><div><p class="MsoNormal"><u></u> <u></u></p></div><div><div><p class="MsoNormal">Hi <span style="font-size:10.5pt;font-family:Helvetica;color:rgb(32,33,36);letter-spacing:0.15pt">Jörg and other interested people,</span><u></u><u></u></p></div><div><p class="MsoNormal"><u></u> <u></u></p></div><p class="MsoNormal">There is just one piece missing I think for the usage of Interfaces that I am looking for. I would like to be able to write a routine that doesn't know what Data is (in the provided example) but can still hold a pointer to it or to anything else that implements the JSONify interface (and not anything that doesn't implement the JSONify interface) and can call it's ToJSON procedure.<u></u><u></u></p><div><p class="MsoNormal"><u></u> <u></u></p></div><div><p class="MsoNormal">In my previous reply I incorrectly thought that a record in Oberon can be an extension of two base records, which is not right, and not what Jörg is presenting. <u></u><u></u></p></div><div><p class="MsoNormal"><u></u> <u></u></p></div><div><p class="MsoNormal">Apologies for my confusion,<u></u><u></u></p></div><div><p class="MsoNormal">Chuck<u></u><u></u></p></div><div><p class="MsoNormal"><u></u> <u></u></p></div></div><p class="MsoNormal"><u></u> <u></u></p><div><div><p class="MsoNormal">On Mon, Oct 26, 2020 at 7:37 AM Charles Perkins <<a href="mailto:chuck@kuracali.com" target="_blank">chuck@kuracali.com</a>> wrote:<u></u><u></u></p></div><blockquote style="border-top:none;border-right:none;border-bottom:none;border-left:1pt solid rgb(204,204,204);padding:0cm 0cm 0cm 6pt;margin-left:4.8pt;margin-right:0cm"><div><p class="MsoNormal">Hi <span style="font-size:10.5pt;font-family:Helvetica;color:rgb(32,33,36);letter-spacing:0.15pt">Jörg,</span><u></u><u></u></p><div><p class="MsoNormal"><u></u> <u></u></p></div><div><p class="MsoNormal"><span style="font-size:10.5pt;font-family:Helvetica;color:rgb(32,33,36);letter-spacing:0.15pt">With this example I think I understand how Oberon can in fact already do what I want in composing Interfaces. In Wirthian fashion the mechanism is explicit rather than implicit. I had not made the mental jump to how a record may be based on more than one base record... with that, everything else falls into place.</span><u></u><u></u></p></div><div><p class="MsoNormal"><u></u> <u></u></p></div><div><p class="MsoNormal"><span style="font-size:10.5pt;font-family:Helvetica;color:rgb(32,33,36);letter-spacing:0.15pt">I observe that this means that interfaces are 'opt in' rather than automatic based on just matching a subset of method names, but that's typical and arguably more safe anyway.</span><u></u><u></u></p></div><div><p class="MsoNormal"><u></u> <u></u></p></div><div><p class="MsoNormal"><span style="font-size:10.5pt;font-family:Helvetica;color:rgb(32,33,36);letter-spacing:0.15pt">I'm going to think on this some more.</span><u></u><u></u></p></div><div><p class="MsoNormal"><u></u> <u></u></p></div><div><p class="MsoNormal"><span style="font-size:10.5pt;font-family:Helvetica;color:rgb(32,33,36);letter-spacing:0.15pt">Thank you very much for taking the time to illustrate this to us.</span><u></u><u></u></p></div><div><p class="MsoNormal"><u></u> <u></u></p></div><div><p class="MsoNormal"><span style="font-size:10.5pt;font-family:Helvetica;color:rgb(32,33,36);letter-spacing:0.15pt">Best,</span><u></u><u></u></p></div><div><p class="MsoNormal"><span style="font-size:10.5pt;font-family:Helvetica;color:rgb(32,33,36);letter-spacing:0.15pt">Chuck</span><u></u><u></u></p><div><p class="MsoNormal"><u></u> <u></u></p></div></div></div><p class="MsoNormal"><u></u> <u></u></p><div><div><p class="MsoNormal">On Sun, Oct 25, 2020 at 11:55 PM Jörg <<a href="mailto:joerg.straube@iaeth.ch" target="_blank">joerg.straube@iaeth.ch</a>> wrote:<u></u><u></u></p></div><blockquote style="border-top:none;border-right:none;border-bottom:none;border-left:1pt solid rgb(204,204,204);padding:0cm 0cm 0cm 6pt;margin-left:4.8pt;margin-right:0cm"><p class="MsoNormal">Chuck<br><br>        > Andreas, in your scheme can you create separate protocols, for example<br>        > "Jsonify" with the method ToJSON and a different protocol "Persistify" with<br>        > the methods "Store" and "Load", and have other records implement one or the<br>        > other or neither or both?<br><br>Here my proposal for doing this in standard Oberon-07.<br>In the previous mail, I separated interface/protocol and implementation for clarity. The same should be done here. For brevity, I combined the two here.<br><br>MODULE Data;<br>(* Definition of whatever your internal data structure looks like. Here just an example *)<br>TYPE<br>  Tree* = POINTER TO TreeDesc;<br>  TreeDesc* = RECORD val*: ARRAY 15 OF CHAR; left*, right*: Tree END<br>END Data.<br><br>MODULE Jsonify;<br>IMPORT Data;<br>TYPE<br>  Methods* = POINTER TO MDesc;<br>  MDesc* = RECORD<br>    toJSON: PROCEDURE(this: Data.Tree);<br>  END;<br>  (* empty or default implementation *)<br>  PROCEDURE J(this: Data.Tree); END J;<br>  PROCEDURE New*(VAR m: Method); BEGIN NEW(m); m.toJSON := J END;<br>END Jsonify.<br><br>MODULE Persistify;<br>IMPORT Data;<br>TYPE<br>  Methods* = POINTER TO MDesc;<br>  MDesc* = RECORD<br>    Load: PROCEDURE(VAR this: Data.Tree);<br>    Store: PROCEDURE(this: Data.Tree)<br>  END;<br>  (* empty or default implementation *)<br>  PROCEDURE L(VAR this: Data.Tree); BEGIN this := NIL END L;<br>  PROCEDURE S(this: Data.Tree); END S;<br>  PROCEDURE New*(VAR m: Methods); BEGIN NEW(m); m.Load := L; m.Store := S END Init;<br>END Persistify.<br><br>Here now a module using both interfaces/protocols and overwrite even one persist procedure with an own version, if wanted. <br>MODULE Usage;<br>IMPORT Data, Jsonify, Persistify;<br>TYPE<br>  User = RECORD (Data.Tree)<br>    j: Jsonify;<br>    p: Persistify<br>  END;<br>VAR u: User;<br>PROCEDURE myLoad(this: Data.Tree); (* implement your version of persist Load *) END myLoad;<br>PROCEDURE New*(VAR u: User);<br>  BEGIN NEW(u); Jsonify.New(u.j); Persistify.New(u.p); u.p.Load := myLoad END New;<br><br>BEGIN<br>  New(u); u.j.toJSON(u); u.p.Load(u) (* this calls my version *)<br>END Usage.<br><br>Adding the qualifiers "j" and "p" circumvents the ambiguity in case the two interfaces defined methods with the same name.<br><br>br<br>Jörg<br><br><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><u></u><u></u></p></blockquote></div></blockquote></div><p class="MsoNormal">-- <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> <u></u><u></u></p></div></div>
--<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" rel="noreferrer" target="_blank">https://lists.inf.ethz.ch/mailman/listinfo/oberon</a><br>
</blockquote></div>