<div dir="ltr">Hi <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,</span><div><font color="#202124" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif"><span style="font-size:14px;letter-spacing:0.2px;white-space:nowrap"><br></span></font></div><div><font color="#202124" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif"><span style="font-size:14px;letter-spacing:0.2px;white-space:nowrap">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></font></div><div><font color="#202124" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif"><span style="font-size:14px;letter-spacing:0.2px;white-space:nowrap"><br></span></font></div><div><font color="#202124" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif"><span style="font-size:14px;letter-spacing:0.2px;white-space:nowrap">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></font></div><div><font color="#202124" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif"><span style="font-size:14px;letter-spacing:0.2px;white-space:nowrap"><br></span></font></div><div><font color="#202124" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif"><span style="font-size:14px;letter-spacing:0.2px;white-space:nowrap">I'm going to think on this some more.</span></font></div><div><font color="#202124" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif"><span style="font-size:14px;letter-spacing:0.2px;white-space:nowrap"><br></span></font></div><div><font color="#202124" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif"><span style="font-size:14px;letter-spacing:0.2px;white-space:nowrap">Thank you very much for taking the time to illustrate this to us.</span></font></div><div><font color="#202124" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif"><span style="font-size:14px;letter-spacing:0.2px;white-space:nowrap"><br></span></font></div><div><font color="#202124" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif"><span style="font-size:14px;letter-spacing:0.2px;white-space:nowrap">Best,</span></font></div><div><font color="#202124" face="Roboto, RobotoDraft, Helvetica, Arial, sans-serif"><span style="font-size:14px;letter-spacing:0.2px;white-space:nowrap">Chuck<br></span></font><div><span style="color:rgb(136,136,136)"><br></span></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">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:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">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" rel="noreferrer" target="_blank">https://lists.inf.ethz.ch/mailman/listinfo/oberon</a><br>
</blockquote></div>