Skip to content

Commit 5d7a648

Browse files
committed
Small improvements to C4 documentation
1 parent 7a453ba commit 5d7a648

File tree

2 files changed

+29
-24
lines changed
  • doc/reference/gerbil/runtime
  • src/gerbil/runtime

2 files changed

+29
-24
lines changed

doc/reference/gerbil/runtime/c3.md

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ constraints, omitting to count the 4th, yet implicitly respecting it:
3232
Additionally, C4 extends C3 with support for structs that require single-inheritance,
3333
by adding the following constraint:
3434

35-
5. *Struct Suffix* (first implicitly recognized by Scala 3?):
35+
5. *Struct Suffix* (first implicitly recognized by Ruby, and later by Scala 3):
3636
A struct's precedence list is a suffix
3737
of the precedence list of its subclasses.
3838

@@ -48,24 +48,28 @@ the precedence list in cases that multiple solutions satisfy the above constrain
4848
this is equivalent to building the precedence list tail-first with the reverse traversal.
4949
A different traversal for instance would be depth-first, but there are good reasons to pick
5050
depth-first, notably in perserving as much as possible the tail of the precedence list,
51-
thereby maximizing sharing of partial semantics in methods and slots.
51+
thereby maximizing sharing of partial semantics in methods and slots
52+
even when the tail is not explicitly declared as being part of a struct.
5253

5354
*NB*: C3 was adopted by many modern multiple inheritance object systems for its consistency properties:
5455
OpenDylan, Python 2.3, Raku, Parrot, Solidity, PGF/TikZ.
55-
Our 5th constraint has been adopted by Scala 3
56-
(that fails to otherwise use C3 or offer its consistency).
57-
But as far as we know, as of May 2025, Gerbil Scheme 0.18.2 is the only language adopting both.
56+
Our 5th constraint has been adopted by Ruby and Scala 3,
57+
that fail to otherwise use C3 or offer its consistency.
58+
59+
But as far as we know, as of May 2025, Gerbil Scheme 0.18.2 is the only language
60+
respecting all these constraints.
5861

5962
*PS*: Common Lisp, and the earlier tradition of multiple inheritance, that we
60-
follow here, all the way back to Flavors, calls "classes" the things with
61-
multiple inheritance and "structs" the things with single inheritance only.
62-
Smalltalk and after it Java, and the earlier and historically more prevalent
63-
tradition of single inheritance, calls "classes" the things with single
64-
inheritance and "traits" the things with multiple inheritance (after Mesa).
65-
C++ only has multiple inheritance (though duplicating non-virtual superclasses
66-
behave more like mixin inheritance), and calls "struct" a class where all
67-
"members" are public by default. Wonderful nomenclature, right?
68-
We here stick to the Common Lisp namings.
63+
follow here, all the way back to Flavors, calls "classes" the entities with
64+
multiple inheritance and "structs" the entities with single inheritance only.
65+
Smalltalk and after it Java and Scala, and the earlier and historically more prevalent
66+
tradition of single inheritance, calls "classes" the entities with single
67+
inheritance and "traits" the entities with multiple inheritance (after Mesa);
68+
as for Ruby, it calls them respectively "classes" and "modules".
69+
C++ only has its own limited variant of "Multiple Inheritance"
70+
(though duplicating non-virtual superclasses behave more like mixin inheritance),
71+
and calls "struct" a class where all "members" are public by default.
72+
Wonderful nomenclature, right? We here stick to the Common Lisp namings.
6973

7074
## Procedures
7175

src/gerbil/runtime/c3.ss

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ namespace: #f
1414
(export c4-linearize)
1515
(import "util")
1616

17-
;; C5 linearization algorithm: given a top object x from which to compute the precedence list,
17+
;; C4 linearization algorithm: given a top object x from which to compute the precedence list,
1818
;; - rhead is the reverse of a prefix for the precedence list, typically [x] or []
1919
;; depending on whether to include x as head of the result.
2020
;; - supers the list of direct supers of x, typically (get-supers x)
2121
;; - get-precedence-list gets the precedence list for a super s, including s itself in front
2222
;; - struct is a predicate that tells if a class follows single inheritance, and
2323
;; must have its precedence list be a suffix of any subclass' precedence list.
24-
;; - eq is an equality predicate between list elements
25-
;; - get-name gets the name of a object/class, for debugging only.
24+
;; - eq is an equality predicate between list elements (defaults to eq?).
25+
;; - get-name gets the name of a object/class, for debugging only (defaults to identity).
2626
;; Returns the linearized precedence list, and the most specific struct superclass if any
2727
;; : (List X) (List X) \
2828
;; get-precedence-list: (X -> (NonEmptyList X)) \
@@ -38,23 +38,24 @@ namespace: #f
3838
=> :values
3939

4040
(cond
41-
((null? supers) ;; 0 direct superclass: base class
41+
((null? supers) ;; 0 direct superclass: it's a base class
4242
(values (reverse rhead) #f))
43-
((null? (cdr supers)) ;; 1 direct superclass: effective single inheritance
43+
((null? (cdr supers)) ;; 1 direct superclass: it's effectively single inheritance
4444
(let (pl (get-precedence-list (car supers)))
4545
(values (append-reverse rhead pl)
4646
(find struct? pl))))
47-
(else ;; 2 direct superclasses or more: effective multiple inheritance
47+
(else ;; 2 direct superclasses or more: it's effectively multiple inheritance
4848
(let ((pls (map get-precedence-list supers)) ;; (List (List X)) ;; precedence lists to merge
4949
(sis [])) ;; (List X) ;; single-inheritance suffix
5050

5151
;; Split every precedence list at the first struct, consider whatever
5252
;; follows as a suffix of the precedence-list. Merge all the suffixes,
5353
;; where two suffixes are compatible if one is a suffix of the other.
54-
;; Then in each remaining precedence list, (a) remove from the end the
54+
;; Then in each remaining precedence list, remove from the end the
5555
;; classes that are in the correct order in the suffix, until you reach one
56-
;; that isn't in the suffix, then check that no more class there is in the
57-
;; suffix (or else there's an incompatibility).
56+
;; that isn't in the suffix, then check that no more class remains in the list
57+
;; that is in the suffix (or else there's an incompatibility, as in
58+
;; this example bug: direct supers (A) (S A) (B A S)).
5859
;; Use that as suffix of the precedence list,
5960
;; and for the (reverse) head, proceed as usual with C3.
6061

@@ -162,7 +163,7 @@ namespace: #f
162163
(set-car! t tail))
163164
(loop more)))))
164165

165-
;; Now for the regular C3 loop
166+
;; The regular C3 loop
166167
;; NB: if we cached the lengths of the precedence lists,
167168
;; we could walk the precedence list to check which longest tail has the same length
168169
;; as that of the precedence list of its top element, thereby being that very same list,

0 commit comments

Comments
 (0)