<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;}
.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;}
--></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'>Okay. My proposal in standard Oberon-07 for your new requirement is this:<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'>MODULE Data;<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'> Any* = POINTER TO Empty;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> Empty* = RECORD END;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'>END Data.<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>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 style='mso-fareast-language:EN-US'><o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US>MODULE myData;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US>IMPORT Data;<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'> Tree* = POINTER TO TreeDesc;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> TreeDesc* = RECORD (Data.Empty)<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> left, right: Tree;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US style='mso-fareast-language:EN-US'> j: Jsonify;<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>VAR t: Tree;<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US>PROCEDURE J(t: Data.Any);<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US> BEGIN<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US> CASE t OF Tree: (* implement here your Tree-specific JSON routine *) <o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US> END<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US> END J;<o:p></o:p></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><o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US>BEGIN<o:p></o:p></span></p><p class=MsoNormal><span lang=EN-US> </span> New(t); t.j.toJSON(t)<o:p></o:p></p><p class=MsoNormal><span lang=EN-US>END myData.</span><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'><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>Montag, 26. Oktober 2020 um 17:03<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><div><p class=MsoNormal>Hi <span style='font-size:10.5pt;font-family:Helvetica;color:#202124;letter-spacing:.15pt'>Jörg and other interested people,</span><o:p></o:p></p></div><div><p class=MsoNormal><o:p> </o:p></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.<o:p></o:p></p><div><p class=MsoNormal><o:p> </o:p></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. <o:p></o:p></p></div><div><p class=MsoNormal><o:p> </o:p></p></div><div><p class=MsoNormal>Apologies for my confusion,<o:p></o:p></p></div><div><p class=MsoNormal>Chuck<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 Mon, Oct 26, 2020 at 7:37 AM Charles Perkins <<a href="mailto:chuck@kuracali.com">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-right:0cm'><div><p class=MsoNormal>Hi <span style='font-size:10.5pt;font-family:Helvetica;color:#202124;letter-spacing:.15pt'>Jörg,</span><o:p></o:p></p><div><p class=MsoNormal><o:p> </o:p></p></div><div><p class=MsoNormal><span style='font-size:10.5pt;font-family:Helvetica;color:#202124;letter-spacing:.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><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;font-family:Helvetica;color:#202124;letter-spacing:.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><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;font-family:Helvetica;color:#202124;letter-spacing:.15pt'>I'm going to think on this some more.</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;font-family:Helvetica;color:#202124;letter-spacing:.15pt'>Thank you very much for taking the time to illustrate this to us.</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;font-family:Helvetica;color:#202124;letter-spacing:.15pt'>Best,</span><o:p></o:p></p></div><div><p class=MsoNormal><span style='font-size:10.5pt;font-family:Helvetica;color:#202124;letter-spacing:.15pt'>Chuck</span><o:p></o:p></p><div><p class=MsoNormal><o:p> </o:p></p></div></div></div><p class=MsoNormal><o:p> </o:p></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:<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'><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><o:p></o:p></p></blockquote></div></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>