Implement functional record setters.
[bpt/guile.git] / module / srfi / srfi-9 / gnu.scm
CommitLineData
e525e4e4
NI
1;;; Extensions to SRFI-9
2
d9e36897 3;; Copyright (C) 2010, 2012 Free Software Foundation, Inc.
e525e4e4
NI
4;;
5;; This library is free software; you can redistribute it and/or
6;; modify it under the terms of the GNU Lesser General Public
7;; License as published by the Free Software Foundation; either
8;; version 3 of the License, or (at your option) any later version.
9;;
10;; This library is distributed in the hope that it will be useful,
11;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13;; Lesser General Public License for more details.
14;;
15;; You should have received a copy of the GNU Lesser General Public
16;; License along with this library; if not, write to the Free Software
17;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19;;; Commentary:
20
21;; Extensions to SRFI-9. Fully documented in the Guile Reference Manual.
22
23;;; Code:
24
25(define-module (srfi srfi-9 gnu)
d9e36897
MW
26 #:use-module (srfi srfi-1)
27 #:export (set-record-type-printer!
28 define-immutable-record-type
29 set-field
30 set-fields))
e525e4e4 31
167510bc
NI
32(define (set-record-type-printer! type thunk)
33 "Set a custom printer THUNK for TYPE."
6942d864 34 (struct-set! type vtable-index-printer thunk))
d9e36897
MW
35
36(define-syntax-rule (define-immutable-record-type name ctor pred fields ...)
37 ((@@ (srfi srfi-9) %define-record-type) #t name ctor pred fields ...))
38
39(define-syntax-rule (set-field (getter ...) s expr)
40 (%set-fields #t (set-field (getter ...) s expr) ()
41 s ((getter ...) expr)))
42
43(define-syntax-rule (set-fields s . rest)
44 (%set-fields #t (set-fields s . rest) ()
45 s . rest))
46
47;;
48;; collate-set-field-specs is a helper for %set-fields
49;; thats combines all specs with the same head together.
50;;
51;; For example:
52;;
53;; SPECS: (((a b c) expr1)
54;; ((a d) expr2)
55;; ((b c) expr3)
56;; ((c) expr4))
57;;
58;; RESULT: ((a ((b c) expr1)
59;; ((d) expr2))
60;; (b ((c) expr3))
61;; (c (() expr4)))
62;;
63(define (collate-set-field-specs specs)
64 (define (insert head tail expr result)
65 (cond ((find (lambda (tree)
66 (free-identifier=? head (car tree)))
67 result)
68 => (lambda (tree)
69 `((,head (,tail ,expr)
70 ,@(cdr tree))
71 ,@(delq tree result))))
72 (else `((,head (,tail ,expr))
73 ,@result))))
74 (with-syntax (((((head . tail) expr) ...) specs))
75 (fold insert '() #'(head ...) #'(tail ...) #'(expr ...))))
76
77(define-syntax %set-fields-unknown-getter
78 (lambda (x)
79 (syntax-case x ()
80 ((_ orig-form getter)
81 (syntax-violation 'set-fields "unknown getter" #'orig-form #'getter)))))
82
83(define-syntax %set-fields
84 (lambda (x)
85 (with-syntax ((getter-type #'(@@ (srfi srfi-9) getter-type))
86 (getter-index #'(@@ (srfi srfi-9) getter-index))
87 (getter-copier #'(@@ (srfi srfi-9) getter-copier)))
88 (syntax-case x ()
89 ((_ check? orig-form (path-so-far ...)
90 s)
91 #'s)
92 ((_ check? orig-form (path-so-far ...)
93 s (() e))
94 #'e)
95 ((_ check? orig-form (path-so-far ...)
96 struct-expr ((head . tail) expr) ...)
97 (let ((collated-specs (collate-set-field-specs
98 #'(((head . tail) expr) ...))))
99 (with-syntax ((getter (caar collated-specs)))
100 (with-syntax ((err #'(%set-fields-unknown-getter
101 orig-form getter)))
102 #`(let ((s struct-expr))
103 ((getter-copier getter err)
104 check?
105 s
106 #,@(map (lambda (spec)
107 (with-syntax (((head (tail expr) ...) spec))
108 (with-syntax ((err #'(%set-fields-unknown-getter
109 orig-form head)))
110 #'(head (%set-fields
111 check?
112 orig-form
113 (path-so-far ... head)
114 (struct-ref s (getter-index head err))
115 (tail expr) ...)))))
116 collated-specs)))))))
117 ((_ check? orig-form (path-so-far ...)
118 s (() e) (() e*) ...)
119 (syntax-violation 'set-fields "duplicate field path"
120 #'orig-form #'(path-so-far ...)))
121 ((_ check? orig-form (path-so-far ...)
122 s ((getter ...) expr) ...)
123 (syntax-violation 'set-fields "one field path is a prefix of another"
124 #'orig-form #'(path-so-far ...)))
125 ((_ check? orig-form . rest)
126 (syntax-violation 'set-fields "invalid syntax" #'orig-form))))))