1 ;;; srfi-9.scm --- define-record-type
3 ;; Copyright (C) 2001, 2002, 2006, 2009, 2010, 2011, 2012,
4 ;; 2013, 2014 Free Software Foundation, Inc.
6 ;; This library is free software; you can redistribute it and/or
7 ;; modify it under the terms of the GNU Lesser General Public
8 ;; License as published by the Free Software Foundation; either
9 ;; version 3 of the License, or (at your option) any later version.
11 ;; This library is distributed in the hope that it will be useful,
12 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;; Lesser General Public License for more details.
16 ;; You should have received a copy of the GNU Lesser General Public
17 ;; License along with this library; if not, write to the Free Software
18 ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 ;; This module exports the syntactic form `define-record-type', which
23 ;; is the means for creating record types defined in SRFI-9.
25 ;; The syntax of a record type definition is:
27 ;; <record type definition>
28 ;; -> (define-record-type <type name>
29 ;; (<constructor name> <field tag> ...)
33 ;; <field spec> -> (<field tag> <getter name>)
34 ;; -> (<field tag> <getter name> <setter name>)
36 ;; <field tag> -> <identifier>
37 ;; <... name> -> <identifier>
41 ;; guile> (use-modules (srfi srfi-9))
42 ;; guile> (define-record-type :foo (make-foo x) foo?
43 ;; (x get-x) (y get-y set-y!))
44 ;; guile> (define f (make-foo 1))
49 ;; guile> (set-y! f 2)
62 (define-module (srfi srfi-9)
63 #:use-module (srfi srfi-1)
64 #:use-module (system base ck)
65 #:export (define-record-type))
67 (cond-expand-provide (current-module) '(srfi-9))
69 ;; Roll our own instead of using the public `define-inlinable'. This is
70 ;; because the public one has a different `make-procedure-name', so
71 ;; using it would require users to recompile code that uses SRFI-9. See
72 ;; <http://lists.gnu.org/archive/html/guile-devel/2011-04/msg00111.html>.
75 (define-syntax-rule (define-inlinable (name formals ...) body ...)
76 (define-tagged-inlinable () (name formals ...) body ...))
78 ;; 'define-tagged-inlinable' has an additional feature: it stores a map
79 ;; of keys to values that can be retrieved at expansion time. This is
80 ;; currently used to retrieve the rtd id, field index, and record copier
81 ;; macro for an arbitrary getter.
83 (define-syntax-rule (%%on-error err) err)
85 (define %%type #f) ; a private syntax literal
86 (define-syntax getter-type
89 (getter (%%on-error err) %%type s))))
91 (define %%index #f) ; a private syntax literal
92 (define-syntax getter-index
95 (getter (%%on-error err) %%index s))))
97 (define %%copier #f) ; a private syntax literal
98 (define-syntax getter-copier
101 (getter (%%on-error err) %%copier s))))
103 (define-syntax define-tagged-inlinable
105 (define (make-procedure-name name)
107 (symbol-append '% (syntax->datum name)
111 ((_ ((key value) ...) (name formals ...) body ...)
113 (with-syntax ((proc-name (make-procedure-name #'name))
114 ((args ...) (generate-temporaries #'(formals ...))))
116 (define (proc-name formals ...)
120 (syntax-case x (%%on-error key ...)
121 ((_ (%%on-error err) key s) #'(ck s 'value)) ...
123 #'((lambda (formals ...)
127 (syntax-violation 'name "Wrong number of arguments" x))
130 #'proc-name))))))))))
132 (define (default-record-printer s p)
134 (display (record-type-name (record-type-descriptor s)) p)
135 (let loop ((fields (record-type-fields (record-type-descriptor s)))
138 ((not (null? fields))
140 (display (car fields) p)
142 (write (struct-ref s off) p)
143 (loop (cdr fields) (+ 1 off)))))
146 (define-syntax-rule (throw-bad-struct s who)
148 (throw 'wrong-type-arg who
149 "Wrong type argument: ~S" (list s*)
152 (define (make-copier-id type-name)
153 (datum->syntax type-name
154 (symbol-append '%% (syntax->datum type-name)
157 (define-syntax %%set-fields
160 ((_ type-name (getter-id ...) check? s (getter expr) ...)
161 (every identifier? #'(getter ...))
162 (let ((copier-name (syntax->datum (make-copier-id #'type-name)))
163 (getter+exprs #'((getter expr) ...))
164 (nfields (length #'(getter-id ...))))
165 (define (lookup id default-expr)
167 (filter (lambda (g+e)
168 (free-identifier=? id (car g+e)))
170 (case (length results)
172 ((1) (cadar results))
173 (else (syntax-violation
174 copier-name "duplicate getter" x id)))))
175 (for-each (lambda (id)
176 (or (find (lambda (getter-id)
177 (free-identifier=? id getter-id))
180 copier-name "unknown getter" x id)))
182 (with-syntax ((unsafe-expr
183 #`(let ((new (allocate-struct type-name #,nfields)))
184 #,@(map (lambda (getter index)
189 #`(struct-ref s #,index))))
193 (if (syntax->datum #'check?)
194 #`(if (eq? (struct-vtable s) type-name)
197 s '#,(datum->syntax #'here copier-name)))
200 (define-syntax %define-record-type
202 (define (field-identifiers field-specs)
203 (map (lambda (field-spec)
204 (syntax-case field-spec ()
205 ((name getter) #'name)
206 ((name getter setter) #'name)))
209 (define (getter-identifiers field-specs)
210 (map (lambda (field-spec)
211 (syntax-case field-spec ()
212 ((name getter) #'getter)
213 ((name getter setter) #'getter)))
216 (define (constructor form type-name constructor-spec field-ids)
217 (syntax-case constructor-spec ()
219 (every identifier? #'(field ...))
220 (let ((slots (map (lambda (field)
221 (or (list-index (lambda (x)
222 (free-identifier=? x field))
227 (syntax->datum #'macro)))
228 "unknown field in constructor spec"
231 #`(define-inlinable #,constructor-spec
232 (let ((s (allocate-struct #,type-name #,(length field-ids))))
233 #,@(map (lambda (arg slot)
234 #`(struct-set! s #,slot #,arg))
238 (define (getters type-name getter-ids copier-id)
239 (map (lambda (getter index)
240 #`(define-tagged-inlinable
241 ((%%type #,type-name)
243 (%%copier #,copier-id))
245 (if (eq? (struct-vtable s) #,type-name)
246 (struct-ref s #,index)
247 (throw-bad-struct s '#,getter))))
249 (iota (length getter-ids))))
251 (define (copier type-name getter-ids copier-id)
252 #`(define-syntax-rule
253 (#,copier-id check? s (getter expr) (... ...))
254 (%%set-fields #,type-name #,getter-ids
255 check? s (getter expr) (... ...))))
257 (define (setters type-name field-specs)
258 (filter-map (lambda (field-spec index)
259 (syntax-case field-spec ()
261 ((name getter setter)
262 #`(define-inlinable (setter s val)
263 (if (eq? (struct-vtable s) #,type-name)
264 (struct-set! s #,index val)
265 (throw-bad-struct s 'setter))))))
267 (iota (length field-specs))))
269 (define (functional-setters copier-id field-specs)
270 (filter-map (lambda (field-spec index)
271 (syntax-case field-spec ()
273 ((name getter setter)
274 #`(define-inlinable (setter s val)
275 (#,copier-id #t s (getter val))))))
277 (iota (length field-specs))))
279 (define (record-layout immutable? count)
280 ;; Mutability is expressed on the record level; all structs in the
281 ;; future will be mutable.
282 (string-concatenate (make-list count "pw")))
285 ((_ immutable? form type-name constructor-spec predicate-name
288 (define (syntax-error message subform)
289 (syntax-violation (syntax-case #'form ()
290 ((macro . args) (syntax->datum #'macro)))
291 message #'form subform))
292 (and (boolean? (syntax->datum #'immutable?))
293 (or (identifier? #'type-name)
294 (syntax-error "expected type name" #'type-name))
295 (syntax-case #'constructor-spec ()
297 (every identifier? #'(ctor args ...))
299 (_ (syntax-error "invalid constructor spec"
300 #'constructor-spec)))
301 (or (identifier? #'predicate-name)
302 (syntax-error "expected predicate name" #'predicate-name))
303 (every (lambda (spec)
306 ((field getter setter) #t)
307 (_ (syntax-error "invalid field spec" spec))))
308 #'(field-spec ...))))
309 (let* ((field-ids (field-identifiers #'(field-spec ...)))
310 (getter-ids (getter-identifiers #'(field-spec ...)))
311 (field-count (length field-ids))
312 (immutable? (syntax->datum #'immutable?))
313 (layout (record-layout immutable? field-count))
314 (ctor-name (syntax-case #'constructor-spec ()
315 ((ctor args ...) #'ctor)))
316 (copier-id (make-copier-id #'type-name)))
318 #,(constructor #'form #'type-name #'constructor-spec field-ids)
321 (let ((rtd (make-struct/no-tail
323 '#,(datum->syntax #'here (make-struct-layout layout))
324 default-record-printer
327 (set-struct-vtable-name! rtd 'type-name)
328 (struct-set! rtd (+ 2 vtable-offset-user) #,ctor-name)
331 (define-inlinable (predicate-name obj)
333 (eq? (struct-vtable obj) type-name)))
335 #,@(getters #'type-name getter-ids copier-id)
336 #,(copier #'type-name getter-ids copier-id)
338 (functional-setters copier-id #'(field-spec ...))
339 (setters #'type-name #'(field-spec ...))))))
340 ((_ immutable? form . rest)
342 (syntax-case #'form ()
343 ((macro . args) (syntax->datum #'macro)))
344 "invalid record definition syntax"
347 (define-syntax-rule (define-record-type name ctor pred fields ...)
348 (%define-record-type #f (define-record-type name ctor pred fields ...)
349 name ctor pred fields ...))
351 ;;; srfi-9.scm ends here