(record-let <record-data> ((<variable> <field>) ...) <body>)
Where each <variable> is filled with the corresponding data <field> from <record-data> as in a <let> expression, then the <body> is evaluated with these bindinds added and last expressions is returned. It is an error if the <record-data> does not contain corresponding <fields>.
Notice that this works directly on the data itself and that the data may contain more fields than the one cited in the record-let expression allowing code to be reused for inherited records.
An alternate implementation that uses (<record-data> <record-type-descriptor>) instead of <record-data> above will enable record-let to work entirely at compile-time.
I also think that let-record is maybe a more consistent name.
Replying to [comment:1 cowan]:
This will only work if the field names are available at run time, unless the type is also available at compile time.
Yes, It must keep track of fields name. Some kind of reflection is needed for that to work.
By the way, would it be better if fields are restricted to be symbols only ?
Replying to [comment:2 arcfide]:
An alternate implementation that uses (<record-data> <record-type-descriptor>) instead of <record-data> above will enable record-let to work entirely at compile-time. I also think that let-record is maybe a more consistent name.
I am Ok for whatever name.
By the way notice that if we have only single-inheritance (hierachy of types) it is easy by convention to always use the same index number as parent-type fields. So an interpreter/compiler just has to lookup from the fields name which is the corresponding index for fields.
Replying to [comment:4 medernac]:
By the way notice that if we have only single-inheritance (hierachy of types) it is easy by convention to always use the same index number as parent-type fields. So an interpreter/compiler just has to lookup from the fields name which is the corresponding index for fields.
If we have to do lookups, I'm a little concerned. Lookups can be elided when dealing with a sufficiently smart compiler in many/most cases, but not all, and with interpreters that don't do such optimizations, you will end up having much slower access. Especially when dealing with parents that could come from other libraries, the speed issues become more obvious since it is very hard to do that sort of reasoning in general.
Replying to [comment:4 medernac]:
By the way notice that if we have only single-inheritance (hierachy of types) it is easy by convention to always use the same index number as parent-type fields. So an interpreter/compiler just has to lookup from the fields name which is the corresponding index for fields.
It's worth noting that delegation techniques are strictly more expressive than inheritance based ones and they completely avoid the hair of multiple inheritance (this was shown in an OOPSLA paper somewhere around 1994-1996 but I've forgotten the title). I think it would be lovely to see a programmer-defined aggregate system that supported delegation as the core mechanism rather than any form of inheritance.
The only "methods" we have are getters, setters, and predicates. It's not clear to me what the difference is between delegation and inheritance in that context.
Replying to [comment:7 cowan]:
The only "methods" we have are getters, setters, and predicates. It's not clear to me what the difference is between delegation and inheritance in that context.
The reference to inheritance was the OP's. The difference could be trivial; the issue is what types does a new value become a member of (and expose the interfaces of). I do realize that the proposal we are commenting on contains no such information, and I wonder how OP got to inheritance in the first place. The lack of anything resembling a type algebra, even as simple a one as SI, has often been mentioned as one of SRFI-9's main drawbacks, so I expect that it *will* come up somewhere along the line. In fact it is one of the few areas where I'd be willing to see more complexity come into Thing1.
Replying to [comment:8 kumoyuki]:
To illustrate the idea, here is what I am personnaly using in association with let-record for record definition with types:
(record-constructor <Constructor name> <Type checker name> <Fields name> (<Type> <data> <getter>) ...)Where I view type mainly as predicates. I don't have setters, but I see no problem in adding it. Here are some examples:
;; A flat record (record-constructor make-job job-type? job-fields (real? release job-release) (real? duration job-duration) (string? username job-username)) (define (List-of type?) (lambda (data) (and (list? data) (let loop ((tmp data)) (or (null? tmp) (and (type? (car tmp)) (loop (cdr tmp)))))))) ;; A record with recursive type field (record-constructor Tree-of-integer Tree-of-integer-type? Tree-of-integer-fields (integer? element Tree-of-integer->element) ((List-of Tree-of-integer-type?) subtrees Tree-of-integer->subtrees)) (Tree-of-integer 17 (list (Tree-of-integer 12 '())))But I could also do type parameterization like this:
(record-constructor (<Constructor name> <Type parameter> ...) <Type checker name> <Fields name> (<Type> <data> <getter>) ...)For instance:
(record-constructor (Tree type?) Tree-type? Tree-fields (type? element Tree->get-element) ((List-of (Tree type?)) subtrees Tree->get-subtrees))And then generate records like this:
(define make-integer-tree (Tree integer?)) (make-integer-tree 17 (list (make-integer-tree 12 '()))) (define make-string-tree (Tree string?)) (make-string-tree "foo" (list (make-string-tree "bar" '())))Then let-record easily allows access to fields and I could reuse the same code for records sharing some fields.
We voted no.
This will only work if the field names are available at run time, unless the type is also available at compile time.