1 ;;; srfi-9.scm --- define-record-type
3 ;; Copyright (C) 2001, 2002, 2006, 2009, 2010, 2011, 2012,
4 ;; 2013 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 (throw-bad-struct s who)
147 (throw 'wrong-type-arg who
148 "Wrong type argument: ~S" (list s)
151 (define (make-copier-id type-name)
152 (datum->syntax type-name
153 (symbol-append '%% (syntax->datum type-name)
156 (define-syntax %%set-fields
159 ((_ type-name (getter-id ...) check? s (getter expr) ...)
160 (every identifier? #'(getter ...))
161 (let ((copier-name (syntax->datum (make-copier-id #'type-name)))
162 (getter+exprs #'((getter expr) ...)))
163 (define (lookup id default-expr)
165 (filter (lambda (g+e)
166 (free-identifier=? id (car g+e)))
168 (case (length results)
170 ((1) (cadar results))
171 (else (syntax-violation
172 copier-name "duplicate getter" x id)))))
173 (for-each (lambda (id)
174 (or (find (lambda (getter-id)
175 (free-identifier=? id getter-id))
178 copier-name "unknown getter" x id)))
180 (with-syntax ((unsafe-expr
183 #,@(map (lambda (getter index)
184 (lookup getter #`(struct-ref s #,index)))
186 (iota (length #'(getter-id ...)))))))
187 (if (syntax->datum #'check?)
188 #`(if (eq? (struct-vtable s) type-name)
191 s '#,(datum->syntax #'here copier-name)))
194 (define-syntax %define-record-type
196 (define (field-identifiers field-specs)
197 (map (lambda (field-spec)
198 (syntax-case field-spec ()
199 ((name getter) #'name)
200 ((name getter setter) #'name)))
203 (define (getter-identifiers field-specs)
204 (map (lambda (field-spec)
205 (syntax-case field-spec ()
206 ((name getter) #'getter)
207 ((name getter setter) #'getter)))
210 (define (constructor form type-name constructor-spec field-names)
211 (syntax-case constructor-spec ()
213 (every identifier? #'(field ...))
214 (let ((ctor-args (map (lambda (field)
215 (let ((name (syntax->datum field)))
216 (or (memq name field-names)
220 (syntax->datum #'macro)))
221 "unknown field in constructor spec"
225 #`(define-inlinable #,constructor-spec
226 (make-struct #,type-name 0
227 #,@(map (lambda (name)
228 (assq-ref ctor-args name))
231 (define (getters type-name getter-ids copier-id)
232 (map (lambda (getter index)
233 #`(define-tagged-inlinable
234 ((%%type #,type-name)
236 (%%copier #,copier-id))
238 (if (eq? (struct-vtable s) #,type-name)
239 (struct-ref s #,index)
240 (throw-bad-struct s '#,getter))))
242 (iota (length getter-ids))))
244 (define (copier type-name getter-ids copier-id)
245 #`(define-syntax-rule
246 (#,copier-id check? s (getter expr) (... ...))
247 (%%set-fields #,type-name #,getter-ids
248 check? s (getter expr) (... ...))))
250 (define (setters type-name field-specs)
251 (filter-map (lambda (field-spec index)
252 (syntax-case field-spec ()
254 ((name getter setter)
255 #`(define-inlinable (setter s val)
256 (if (eq? (struct-vtable s) #,type-name)
257 (struct-set! s #,index val)
258 (throw-bad-struct s 'setter))))))
260 (iota (length field-specs))))
262 (define (functional-setters copier-id field-specs)
263 (filter-map (lambda (field-spec index)
264 (syntax-case field-spec ()
266 ((name getter setter)
267 #`(define-inlinable (setter s val)
268 (#,copier-id #t s (getter val))))))
270 (iota (length field-specs))))
272 (define (record-layout immutable? count)
273 (let ((desc (if immutable? "pr" "pw")))
274 (string-concatenate (make-list count desc))))
277 ((_ immutable? form type-name constructor-spec predicate-name
280 (define (syntax-error message subform)
281 (syntax-violation (syntax-case #'form ()
282 ((macro . args) (syntax->datum #'macro)))
283 message #'form subform))
284 (and (boolean? (syntax->datum #'immutable?))
285 (or (identifier? #'type-name)
286 (syntax-error "expected type name" #'type-name))
287 (syntax-case #'constructor-spec ()
289 (every identifier? #'(ctor args ...))
291 (_ (syntax-error "invalid constructor spec"
292 #'constructor-spec)))
293 (or (identifier? #'predicate-name)
294 (syntax-error "expected predicate name" #'predicate-name))
295 (every (lambda (spec)
298 ((field getter setter) #t)
299 (_ (syntax-error "invalid field spec" spec))))
300 #'(field-spec ...))))
301 (let* ((field-ids (field-identifiers #'(field-spec ...)))
302 (getter-ids (getter-identifiers #'(field-spec ...)))
303 (field-count (length field-ids))
304 (immutable? (syntax->datum #'immutable?))
305 (layout (record-layout immutable? field-count))
306 (field-names (map syntax->datum field-ids))
307 (ctor-name (syntax-case #'constructor-spec ()
308 ((ctor args ...) #'ctor)))
309 (copier-id (make-copier-id #'type-name)))
311 #,(constructor #'form #'type-name #'constructor-spec field-names)
314 (let ((rtd (make-struct/no-tail
316 '#,(datum->syntax #'here (make-struct-layout layout))
317 default-record-printer
320 (set-struct-vtable-name! rtd 'type-name)
321 (struct-set! rtd (+ 2 vtable-offset-user) #,ctor-name)
324 (define-inlinable (predicate-name obj)
326 (eq? (struct-vtable obj) type-name)))
328 #,@(getters #'type-name getter-ids copier-id)
329 #,(copier #'type-name getter-ids copier-id)
331 (functional-setters copier-id #'(field-spec ...))
332 (setters #'type-name #'(field-spec ...))))))
333 ((_ immutable? form . rest)
335 (syntax-case #'form ()
336 ((macro . args) (syntax->datum #'macro)))
337 "invalid record definition syntax"
340 (define-syntax-rule (define-record-type name ctor pred fields ...)
341 (%define-record-type #f (define-record-type name ctor pred fields ...)
342 name ctor pred fields ...))
344 ;;; srfi-9.scm ends here