<div dir="ltr">Here is an attempt to summarize the arguments for the two class encodings. Please add or modify as necessary/helpful.<div><br></div><div>
<p class="MsoNormal">How the class got its dots:</p>
<p class="MsoNormal">We have two options for encoding classes in Grace -- as objects or as methods. Suppose we start with the following grace class:</p>
<p class="MsoNormal">class c.new(n’: Number) -> CType {</p>
<p class="MsoNormal"> var n: Number :=
n’</p>
<p class="MsoNormal"> method m(s:
String) -> Done {</p>
<p class="MsoNormal"> print “called m with {s}”</p>
<p class="MsoNormal"> }</p>
<p class="MsoNormal">}</p>
<p class="MsoNormal">where </p>
<p class="MsoNormal">type CType = {</p>
<p class="MsoNormal"> m(s:String) ->
Done</p>
<p class="MsoNormal">}</p>
<p class="MsoNormal">With the object encoding, this is an abbreviation for</p>
<p class="MsoNormal">type CClassType = {<br>
new(n: Number) -> CType<br>
}</p>
<p class="MsoNormal">def c: CClassType = object {</p>
<p class="MsoNormal"> method new(n’:
Number) -> CType {</p>
<p class="MsoNormal"> object {</p>
<p class="MsoNormal"> var n:
Number := n’</p>
<p class="MsoNormal"> method m(s:
String) -> Done {</p>
<p class="MsoNormal"> print
“called m with {s}”</p>
<p class="MsoNormal"> }</p>
<p class="MsoNormal"> }</p>
<p class="MsoNormal">}</p><p class="MsoNormal">With the method encoding this represents</p>
<p class="MsoNormal">method cNew(n’: Number) -> CType {</p>
<p class="MsoNormal"> object {</p>
<p class="MsoNormal"> var n:
Number := n’</p>
<p class="MsoNormal"> method m(s:
String) -> Done {</p>
<p class="MsoNormal"> print
“called m with {s}”</p>
<p class="MsoNormal"> }</p>
<p class="MsoNormal">}</p>
<p class="MsoNormal">This second encoding is obviously simpler, as we get to
leave out the outer “object” in the first encoding. (Note that with the second, we could omit the
“.” between “c” and “new” in the class definition.)</p><p class="MsoNormal"></p>
<p class="MsoNormal"> So, why prefer the first? </p>
<p class="MsoListParagraphCxSpFirst" style>1.<span style="font-size:7pt;line-height:normal;font-family:'Times New Roman'">
</span>Creating an object with the dot notation is like
sending a message with an explicit receiver.
Early on, most method requests are to an explicit receiver so writing
c.new(7) seems more consistent than cNew(7).</p><p class="MsoListParagraphCxSpFirst" style><span style>2.</span><span style="font-size:7pt;line-height:normal;font-family:'Times New Roman'">
</span><span style>Renaming on imports is simpler.</span><span style> </span><span style>Let’s suppose that c is a class in module
“outer”.</span><span style> </span><span style>Then we can write:</span></p><p class="MsoListParagraphCxSpMiddle">
import “outer” as other<br>
<br>
type CType = other.CType<br>
<br>
type CClassType = {<br>
new(n: Number) -> CType<br>
}</p>
<p class="MsoListParagraphCxSpMiddle"><br>
def c: CClassType = other.c<br>
<br>
To do the same with the second encoding we would have to write:<br>
<br>
import “outer” as other<br>
<br>
type CType = other.CType<br>
<br>
method cNew(n’: Number) -> CType {</p>
<p class="MsoListParagraphCxSpMiddle">
other.cNew(n’)<br>
}<br>
<br>
The trade-offs are that the definition of c in the first is more compact,
though we would have to write out the type CClassType (which is more complex
than we would like), while the second requires us to redefine cNew as a new
method that calls the original version.
While this could presumably be optimized away, it probably wouldn’t be
for the near future, so there is likely a slight efficiency penalty. (It’s worth noting that if the type
annotations are omitted then the first is significantly simpler.)</p>
<p class="MsoListParagraphCxSpLast" style>3.<span style="font-size:7pt;line-height:normal;font-family:'Times New Roman'">
</span>Suppose we want a class to have multiple
constructors. With the first encoding we
might write:<br>
<br>
class c.fst(n: Number) -> CType {…}<br>
class c.snd(k: Number) -> CType {<br>
inherits c.fst(… k …)<br>
}<br>
<br>
At the moment this is apparently not legal, but it could be and could result in
an encoding where the object encoding the class has two methods fst and snd
that serve as constructors.<br>
<br>
Instead of this notation, we could also write:<br>
<br>
class c.fst(n: Number) -> CType {<br>
constructor snd(k: Number) -> CType
{<br>
fst(… k …)<br>
}<br>
}<br>
<br>
This might make it easier to do the encoding as it is clear that it can be
added to the object and you don’t have to worry about the scope in which you
can add constructors (Scala does something similar). I would suggest that we restrict this so that
secondary constructors must call the primary constructor. Though this constructor restriction is as
much a matter of good software design as safety.<br>
<br>
With the second encoding we would write this as<br>
def c = object {<br>
fst(n: Number) -> CType {…}<br>
snd(k: Number) -> CType {<br>
self.fst(…k…)<br>
}<br>
}<br>
<br>
The advantage of this encoding is that if d inherits from c, overriding fst,
then snd will automatically adapt to use snd correctly. Andrew uses this in his
structures library. However, this will be very rare, as it will require
constructor fst to take exactly the same arguments as in c. This does happen in Andrew’s structures
library, but I had a hard time trying to find other examples, as typically a
subclass will need more parameters to initialize extra defs or vars.<br>
<br>
The same kind of thing could theoretically be done with the first encoding,
where the “constructor” snd could inherit the code calling fst, but again, it
will be rare that this works. </p>
<p class="MsoNormal">So what is the best solution? I personally still prefer the original object
encoding over the method encoding because it looks more like a method send with
an explicit receiver, however I don’t really see overwhelming evidence for
either direction. Does anyone have any
other arguments for one side or the other?</p>
</div></div>