Truly unifying R6RS and R7RS Daphne Preston-Kendal (04 Oct 2022 18:22 UTC)
Re: Truly unifying R6RS and R7RS Marc Nieper-Wißkirchen (04 Oct 2022 19:16 UTC)
Re: Truly unifying R6RS and R7RS John Cowan (06 Oct 2022 20:30 UTC)
Re: Truly unifying R6RS and R7RS Marc Nieper-Wißkirchen (06 Oct 2022 21:11 UTC)
Re: Truly unifying R6RS and R7RS John Cowan (07 Oct 2022 01:33 UTC)
Re: Truly unifying R6RS and R7RS Marc Nieper-Wißkirchen (07 Oct 2022 08:20 UTC)
Re: Truly unifying R6RS and R7RS Arthur A. Gleckler (07 Oct 2022 18:22 UTC)
Re: Truly unifying R6RS and R7RS John Cowan (07 Oct 2022 22:02 UTC)
Re: Truly unifying R6RS and R7RS Marc Nieper-Wißkirchen (08 Oct 2022 10:37 UTC)
Re: Truly unifying R6RS and R7RS José Bollo (27 Oct 2022 07:30 UTC)
Re: Truly unifying R6RS and R7RS Marc Nieper-Wißkirchen (27 Oct 2022 08:00 UTC)
Re: Truly unifying R6RS and R7RS José Bollo (01 Nov 2022 14:22 UTC)
Re: Truly unifying R6RS and R7RS Marc Nieper-Wißkirchen (01 Nov 2022 14:34 UTC)
Re: Truly unifying R6RS and R7RS José Bollo (03 Nov 2022 08:42 UTC)
(missing)
Fwd: Truly unifying R6RS and R7RS Marc Nieper-Wißkirchen (03 Nov 2022 13:18 UTC)
Re: Truly unifying R6RS and R7RS José Bollo (26 Nov 2022 10:02 UTC)
Re: Truly unifying R6RS and R7RS Marc Nieper-Wißkirchen (26 Nov 2022 17:26 UTC)

Re: Truly unifying R6RS and R7RS Marc Nieper-Wißkirchen 27 Oct 2022 08:00 UTC

Thank you for your email, José.

I do not intend to change names (e.g. "extend" instead of "parent" or
"composition" instead of "inheritance").  If we were in a void when
designing this SRFI, using the best names we could think of would be
the right thing.  However, the advantages of alternative names are
minor (and would clash with the tradition set in SRFI 57/SRFI
76/R6RS/SRFI 99/SRFI 131/SRFI 136/SRFI 150) that infringing backward
compatibility is no option.

You write that you don't care about it because you have no legacy
code.  This is not the case with many other Scheme users who regularly
use the R6RS record system.  The good news is that what you have been
using won't be touched by this SRFI.

You also wrote that R6RS records are "over-complicated".  This is
subjective (but a valid opinion), but you may be interested in what I
wrote here: https://srfi-email.schemers.org/srfi-237/msg/21064822/.
The next draft of this SRFI will show how to simplify the R6RS record
system to address the primary concern of SRFI 99 but without
sacrificing its expressive power.

As for the sq example, it is a question of design and what abstract
data type you actually want to model.  The record facility of this
SRFI gives you the option to realize an abstraction barrier.  If a
program attempts to breach an abstraction barrier (that is part of
your design), the system should be able to disallow this.  (Of course,
your design may be wrong, but this is a different question
altogether.)

The expressiveness of a programming language is not measured by what
you can do with a given piece of code.  Otherwise, machine language
would be the most expressive one. (There are different notions of
"expressiveness"; I tried to use one relevant to Scheme and
programming language theory.)

Thanks,

Marc

Am Do., 27. Okt. 2022 um 09:30 Uhr schrieb José Bollo <xxxxxx@nonadev.net>:
>
> Le Fri, 7 Oct 2022 10:20:19 +0200,
> Marc Nieper-Wißkirchen <xxxxxx@gmail.com> a écrit :
>
> Hi all,
>
> I like your mail Marc, it explains very well the problem.
>
> However I'd like to share some thought about it. But before that, let
> me tell you that my opinion is to let R6 records vanishing because it is
> over complicated and promote implicit naming.
>
> [...]
>
> > First of all, a record type should not be mistaken for a class as it
> > is known from object-oriented languages like Java. (I prepend this
> > comment because much of what is said in favor and against class
> > systems and inheritance in typical OOP languages does not apply to
> > Scheme's record-type system.) A record type does not offer the notion
> > of methods, let alone virtual methods (although the record-type
> > facility can be used to implement more OOP-style oriented class
> > facilities, of course). As far as R7RS-small and R6RS without
> > inheritance are concerned, a record is just like a vector whose
> > elements are not accessed by index but by dedicated procedures and
> > whose type is disjoint from the rest of Scheme types. This allows the
> > creation of new opaque Scheme types whose implementation details are
> > encapsulated so that the resulting type (together with the primitive
> > procedures accessing it) can be viewed as the realization of the
> > concept of an abstract data type in a dynamic language like Scheme.
> > The vast majority of uses of the record-type facility of the small
> > language (and of R6RS) are in this spirit. (For evidence for this, one
> > just has to look at the significant number of SRFIs introducing new
> > types into the Scheme language.)
> >
> > Now let us add inheritance.
>
> I'm thinking that the main problem is here. When you write it, your
> intent is to really add some inheritance. And in the example you are
> using, this is shown because you expect to preserve some kind of class
> invariant.
>
> It may reveal an error in SRFI where words "inheritance" and
> "extend/extension" have to be weighted in the balance and used
> correctly.
>
> > The underlying model is the same in the
> > context of R6RS or any of the various other proposals (SRFI 99, SRFI
> > 131, SRFI 136, SRFI 150), so this simplifies the discussion (and
> > allows me to use the R6RS surface syntax with no loss of generality).
> > Inheritance is just the creation of a new type that is composed of an
> > existing (record) type and a bunch of new fields, where it is arranged
> > so that the accessors and mutators and the predicate of the existing
> > type apply to the composed type as well. In principle, we could
> > therefore go without inheritance by making the composition explicit.
> > So, for example, instead of
> >
> > (define-record-type foo (fields x))
> > (define-record-type bar (parent foo) (fields y))
>
> Here the use of "parent". Why not "extend" or "include"? It is a
> vocabulary details but I think that it can change how things are
> perceived: here you expect paradigms of OOP to apply.
>
> Okay it keeps R6 stuff unchanged. And it seems to be a valuable
> motivation. But I don"t care about it because [1] I have no legacy code
> and [2] scheme language is enough flexible to process the case using an
> import "r6rs".
>
> > we would write
> >
> > (define-record-type (foo make-foo %foo?)
> >   (fields (immutable x %foo-x)))
> > (define-record-type bar
> >   (fields (immutable foo %bar-foo) y)
> >   (protocol
> >     (lambda (p)
> >       (lambda (x y)
> >         (p (make-foo x) y)))))
> > (define (foo? obj) (or (%foo? obj) (bar? obj)))
> > (define (foo-x foo) (if (%foo? foo) (%foo-x foo) (%foo-x (%bar-foo
> > foo))))
> >
> > The reason why we still want inheritance (in the style of the Scheme
> > record-type facilities) although it is not much more than composition
> > is at least two-fold: The above code with explicit composition adds
> > another indirection as can be seen from the definition of `foo-x`.
> > This inefficiency adds up for each further composition (compared to
> > this, the single-inheritance record-type facility can be implemented
> > so that parent- and child-type fields are arranged linearly in
> > memory). Moreover, the contortions needed in the above code so that
> > the accessors and mutators and the type predicate of the "foo" type
> > apply to the "bar" type do not scale well.
>
> Here again question of vocabulary, inheritance is connoted as
> object-oriented. By the way, the word composition is interesting but
> also subject to discussion. In a later mail your wrote:
>
> >------BEGIN OF THE INCLUDED MAIL-------------------
> > Le Sat, 8 Oct 2022 12:37:19 +0200,
> > Marc Nieper-Wißkirchen <xxxxxx@gmail.com> a écrit :
> > ....
> > Inlining cannot eliminate the cost of the extra indirection that is
> > necessary when the types/fields are composed by hand and not using the
> > inheritance mechanism. It's the difference between
> >
> > struct foo
> > {
> >   /* ... */
> > };
> >
> > struct bar
> > {
> >   struct foo *x;
> >   /* ... */
> > };
> >
> > and
> >
> > struct foo
> > {
> >   /* ... */
> > };
> >
> > struct bar
> > {
> >   struct foo x;
> >   /* ... */
> > };
> > ....
> >------END OF THE INCLUDED MAIL-------------------
>
> For me it show the ambiguity of the word "composition". I read in
> literature use of the terms "extend", "compose" and "aggregate". I am
> not sure about what are the exact terms to use. For me extend is the
> mechanism without pointer and aggregate with pointer.
>
> > The straightforward implementation of composition above (the second
> > code example) has one crucial feature we want to carry over to the
> > composition-through-inheritance model of the Scheme record-type
> > facilities.  The "foo" type is an abstract data type (in the sense
> > from the beginning of my explanation) from the viewpoint of the
> > composing data type "bar".  To illustrate this, let us look at a
> > similar (but let me skip the contortions mentioned above because they
> > are not relevant here):
> >
> > (define-record-type sq (fields (immutable x sq-ref) (immutable y
> > sq-sqrt-ref)) (protocol
> >     (lambda (p)
> >       (lambda (x) (p x (sqrt x))))))
> >
> > The "sq" type models a number, of which I can take a square root. The
> > calculation of the square root is cached, but this is an
> > implementation detail and transparent to the user of this record type
> > (viewed as the reflection of an abstract data type). In the explicit
> > composition model, say when we want to implement a "colored-sq", the
> > composing type needs to access the "sq" type solely through its public
> > interface. Thus the abstraction barrier is not breached. (This is an
> > essential point because the definition and implementation of the "sq"
> > type may be in a different library (or package) so the implementer of
> > the "colored-sq" type would or should not know about the caching of
> > the square root.
> >
> > With custom constructors in the sense of R6RS, this abstraction
> > barrier is preserved.  A possible definition of the "colored-sq" type
> > would then be
> >
> > (define-record-type colored-sq (fields color)
> >   (parent sq)
> >   (protocol
> >     (lambda (n)
> >       (lambda (x c)
> >         ((n x) c)))))
> >
> > As we can see, we do not need to access any implementation details of
> > the "sq" type. Without the protocol device, this is not possible,
> > though, and this is the reason for custom protocols: The definition of
> > the "sq" type itself is not a problem. We can instead write
> >
> > (define-record-type (sq %make-sq sq?) (fields (immutable x sq-ref)
> > (immutable y sq-sqrt-ref)))
> > (define (make-sq x) (%sq x (sqrt x)))
> >
> > The problem arises as soon as we want to inherit from it:
> >
> > (define-record-type (colored-sq %make-colored-sq colored-sq?)
> >   (fields color) (parent sq))
> >
> > (define (make-colored-sq x c)
> >   (%make-colored-sq x (sqrt y) c))
> >
> > Obviously, we need to access what should be implementation details of
> > the "sq" type here. The `make-sq` procedure, which should have
> > provided us with the abstraction cannot be used here.
>
> Here is the knot of the argument. The procedure make-colored-sq is
> perfectly correct and clearly implement what is expected. But has you
> wrote it should use some knowledge that might be hidden. And I agree
> that some kind of solution should be offered to programmers to access
> that issue.
>
> But the point of vue that you have is to keep the invariant that sq is
> the sq defined in sq. What if the programmer expects the opposite? The
> assumption that the invariant is kept is valuable and natural. It is
> the most common case. But making it a keystone is not good in my mind.
> Let be flexible and don't close doors.
>
> So conversely, if you are using protocol as proposed, it seems
> difficult to change the value of sq in "inheriter".
>
> I'm stopping here with that argument. I'm sure that something has to be
> done to improve SRFI-136 in order to allow calling constuctor in
> constructor. That could be a good issue for the problem that you
> perfectly exposed.
>
> Best regards
> José
>
>
> > One could try to repair it by having the "sq" package export a
> > procedure like
> >
> > (define (sq-init! sq x)
> >   (sq-set! sq x)
> >   (sq-square-set! sq (sqrt x)))
> >
> > Moreover, a restricted SRFI 9-style custom constructor would be used
> > for "sq":
> >
> > (define-record-type sq
> >   (%make-sq)
> >   sq?
> >   (x sq-ref)
> >   (y sq-square-ref))
> >
> > This way, the `make-colored-sq` procedure can be defined by
> >
> > (define (make-colored-sq x c)
> >   (define rec (%make-colored-sq c))
> >   (sq-init! rec x)
> >   x)
> >
> > This, however, is a non-solution for two reasons: First of all, it
> > forces at least the "y" field of the "sq" type to be mutable, which
> > will prevent some possible optimizations (employed by Chez, for
> > example).  Moreover, this "solution" still breaks the abstraction
> > barrier around the "sq" package because it is would be possible to
> > construct broken objects of the "sq" type (because the invariant that
> > the "y" field holds the square root of the "x" field is broken).
> >
> > I can think of correct solutions outside of R6RS-syle custom protocols
> > to maintain the abstraction barrier around the "sq" package. but these
> > would be no simpler than the R6RS-solution  of custom protocols
> > (instead of exporting the record name "sq" to be used in record-type
> > definitions of child types, the "sq" package would have to define and
> > export a syntax like "define-sq-type" that encapsulates everything
> > related to defining child types). And such a solution would not be
> > integrated into the procedural layer.
> >
> > I hope the above rationale explains the raison d'être of custom
> > protocols for R6RS record types. Unrelated to this, making the
> > definition of custom constructors as simple as possible has another
> > advantage: type checking. So for example,
> >
> > (define-record-type point (fields x y)
> >   (protocol
> >     (lambda (p)
> >       (lambda (x y)
> >         (assert (real? x))
> >         (assert (real? y))
> >         (p x y)))))
> >
> > is much better than `(define-record-type point (fields x y))` to
> > define a type of a point in the cartesian plane. With the latter
> > definition, I can create points that are not true points and when I
> > later get errors, e.g. when I attempt to calculate the distance
> > between two points, the location where the error becomes apparent is
> > not where the error originated, making debugging hard.  Probably the
> > majority of record types have some constraints for the arguments of
> > their constructors, and it is good when the language suggests checking
> > them at construction time.
> >
> > >> I am not convinced that is necessary to provide the second form of
> > >> `define-record-type` with all bells and whistles as long as
> > >> R7RS-small compatibility is there
> > >
> > >
> > > It is of course not *necessary*, but it accommodates R7RS-small
> > > users who wish to make use of one of the R6RS features without
> > > learning the entirely new syntax.
> >
> > All right.
> >
> > [...]
> >
> > > Yes.  So let's adopt the p-list-style extension to R7RS:
> > > (define-record-type (foo parent bar) ...)
> >
> > Yes.
> >
> > >>
> > >>
> > >> But what would it mean to give a protocol (name)?
> > >
> > >
> > > All right, we can do without that (see below).  So `parent`,
> > > `sealed`, `opaque`, and `nongenerative` would be allowed in any
> > > order.
> >
> > See above why this would still make the SRFI 9-style syntax
> > considerably inferior to the R6R-style syntax.
> >
> > >> I am not sure how much this still looks like SRFI 9-syntax when
> > >> there are a lot of entries next to the record name. Would it
> > >> really be better (for SRFI 9-users) than the first syntax given in
> > >> this SRFI?
> > >
> > > Yes, I think so:
> > >
> > > (define-record-type foo
> > >   (make-foo bar baz)
> > >   foo?
> > >   (bar foo-bar-ref)
> > >   (baz foo-baz-ref foo-baz-set!))
> > >
> > > (define-record-type (extfoo parent foo)
> > >   (make-extfoo quux)
> > >    extfoo?
> > >    (quux foo-quux-ref))
> > >
> > > If this extension to R7RS-small syntax is accepted, the question
> > > remaining is whether the purely syntactic features of SRFI-99
> > > should also be provided.  I think the answer is no, per SRFI 131.
> > > The reason is not, as in SRFI 131, in order to allow a syntax-rules
> > > implementation, but simply because SRFI 9 / R7RS-small users are
> > > already accustomed to providing explicit names.
> >
> > I think this is a good suggestion. Those who want implicit names (for
> > whatever reasons) can always use the R6RS-style form of the syntax.
> > And those like Daphne who "hate" implicit names get a domain where
> > explicit names are guaranteed.
> >
> > >
> > >>
> > >> In any case, however, the problem of interpreting field names in
> > >> SRFI 9-style constructors in child record-type definitions
> > >> remains. They cannot refer to parent fields (as SRFI 99 says) for
> > >> R7RS-small compatibility, so the only solution seems to be that
> > >> the names correspond solely to child fields and that the parent
> > >> constructor's formal arguments are implicitly added at the
> > >> beginning.
> > >
> > >
> > > The bulk of all constructors include all the arguments anyway; the
> > > typical case is to hide the complete constructor in a library and
> > > export an ordinary function that does whatever's necessary to set
> > > up the fields correctly.
> > >>
> > >> (define-record-type colored-point
> > >>   (nongenerative) (opaque #t)
> > >>   (parent point)
> > >>   (fields color)
> > >>   (protocol
> > >>     (lambda (n)
> > >>       (lambda (x y c)
> > >>         (assert (memq c '(red green blue))
> > >>         ((n x y) c)))))
> > >>
> > >> What would a translation of this into SRFI 9-style look like?
> > >
> > >
> > > It wouldn't; see above.
> >
> > See above why there should be a way to express it.
> >
> > But I think I have an idea of how to incorporate custom protocols in
> > SRFI 9-style definitions:
> >
> > (define-record-type (sq protocol (lambda (x) (make-sq x (sqrt x))))
> >   (make-sq x y)
> >   sq?
> >   (x sq-ref)
> >   (y sq-sqrt-ref))
> >
> > (define-record-type (colored-sq parent sq protocol (lambda (x c)
> > (assert (color? c)) ((make-colored-sq x) c))
> >   ((make-colored-sq x) c)
> >   colored-sq?
> >   (c colored-sq-color))
> >
> > Here, "x" is a dummy placeholder.  The above translates to the
> > R6RS-style definitions:
> >
> > (define-record-type (sq make-sq sq?)
> >   (fields (immutable x sq-ref) (immutable y sq-square-ref))
> >   (protocol
> >     (lambda (p)
> >       (let ((make-sq (lambda (x y) (p x y))))
> >         (lambda (x) (make-sq x (sqrt x)))))))
> >
> > (define-record-type (colored-sq make-colored-sq colored-sq?)
> >   (fields (immutable c colored-sq-color))
> >   (protocol
> >     (lambda (n)
> >       (let ((make-colored-sq (lambda (x) (lambda (c) ((n x) c)))
> >         (lambda (x c) (assert (color? c)) ((make-colored-sq x) c))))))
> >
> > I hope, the translation is obvious enough to understand from the
> > examples.  The disadvantage of this proposal is that the constructor
> > subform in the SRFI-9-style syntax is no longer a template for the
> > actually defined constructor.
>