ref: c6597f980b2cc154d807fffe9ed4e8e871319cca
parent: 1773b08f7d3860525f55398bf0de2b14bd0171ac
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Wed Apr 16 22:21:50 EDT 2025
defstruct: always keep a default constructor for vec structs
--- a/boot/sl.boot
+++ b/boot/sl.boot
@@ -79,7 +79,7 @@
#fn(get) *properties* :kind *doc-extra* filter #fn("n10<20Q:" #(:doc-fmt))
#fn("n10<20Q:" #(:doc-see)) princ foldl #fn("n20=161:") newline "See also:" #fn("n1A<0=700=21522263:" #(getprop
*formals-list* " ")) "Members:" #fn("n1A<070021522263:" #(getprop *formals-list* " ")) void
- "no help for " #fn(str) " " "" " (undefined)")) defstruct #fn("O10005000*///z6W1000J7021?14W2000;J60q?24W3000J60D?34W4000J60q?44W5000J60D?54IIb<228<230>1_5142224?=5147586518><8>=268@5127288@528=8@51121C60D@C0129C60q@907:2;1528D3T08;S;J70482DQ360q@807:2<51;J404D@4082;3404085;3Z0485DCC08E;3:042=02>52@B08EJ;07:2?51@40858D3:02@0e2@7002Ae283;3\\0483H;3M0483DQ;3:042=2B052;J504838CP;J5048384;J:042C02D52I222E8D18E8H848508@>8?J5148?3G07F02G8?2H8JPe15252@30q42I2Je18F3{02K8F2Le28D3E02M2N2Le22O8Ge2e3@V02P2Q2R2LEe32O0e2e32S2T2Le27U8A51e3e3e3@30qe18H3\xae08D3t02J2K8H2I1e12O2Ve2e12O0e2e12W7X2Yq8B535154e32Z2O0e22O2[e28H<e4e3@d02K8H8E3K02I29e12O8Ee2e12W8B5153@@02I29e12W8B5152e3@30qe12W7\\2]8<8C8B8I8D8F8G8E0>98A525164:" #(#(:constructor
+ "no help for " #fn(str) " " "" " (undefined)")) defstruct #fn("O10005000*///z6W1000J7021?14W2000;J60q?24W3000J60D?34W4000J60q?44W5000J60D?54IIb<228<230>1_5142224?=5147586518><8>=268@5127288@528=8@51121C60D@C0129C60q@907:2;1528D3T08;S;J70482DQ360q@807:2<51;J404D@4082;36040e185;3Z0485DCC08E;3:042=02>52@B08E37085@807:2?518D3:02@0e2@7002Ae283DQ83;3\\0483H;3M0483DQ;3:042=2B052;J504838CP;J5048384;J:042C02D52II222E8D18E8I848508@>8?K514222F8D108B8E>5?L5148?3G07G02H8?2I8KPe15252@30q42J2Ke18F3{02L8F2Me28D3E02N2O2Me22P8Ge2e3@V02Q2R2S2MEe32P0e2e32T2U2Me27V8A51e3e3e3@30qe18I3C02L8I<8L8I=51e3@30qe18D3U02W2P0e22P2Xe28H3808I<@808L8C51e4@30qe12Y7Z2[8<8C8B8J8D8F8G8E0>98A525165:" #(#(:constructor
2 :predicate 4 NIL NIL :type 0 :named 1 :conc-name 3 NIL NIL) vec #0#
#fn("n17005121220A>28552485:" #(cddr #fn(for-each)
#fn("n17002152340q:722324A<25F2605661:" #(member (:read-only)
@@ -89,11 +89,12 @@
error "invalid slot name: " #fn(list*) #fn(sym) #\:))) tokw) separate-doc-from-body #fn(length)
#fn(map) #fn("n10B3500<:0:") list arg-error "invalid struct type: " "structs of type `vec` are always :named T"
#fn(sym) #\? "predicate not possible unless the struct is :named T" struct … "make-" #fn(str) "-"
- #fn("n12002152853=0220E8553@300853<02208552@40232486252627e1A360q@7028Fe292360q@802992e2933<093DC60q@802:93e2943;02;94e2@30q95S;J80495DQS;39042<95e29697P578764:" #(#fn(str-find)
+ #fn("n12002152853=0220E8553@300853<02208552@40232486252627e1A360q@7028Fe292360q@702992P933<093DC60q@802:93e2943;02;94e2@30q95S;J80495DQS;39042<95e29697P578764:" #(#fn(str-find)
"\n\n" #fn(str-sub) "" #fn(str) "\n\n " #fn(append) defstruct :type :named :constructor
- :conc-name :predicate) fmt) sym-set-doc #fn(append) :doc-fmt #fn(nconc) begin def s equal?
- type-of quote and eq? aref = length 1+ %struct% #fn(copy-list) foldr #fn("n2202105201PP:" #(#fn(sym)
- ":")) putprop constructor map-int #fn("n1A<70F05251709205221938652943<0r20i2KM@30022872324q25e3e3953K0269523e2272896e223e3e3@30q292:25e22;2397360K@30E88Me37<2=85523O02>2?2@2886e22A2898e22Be6e2@G02C2397360K@30E88M24e4e4e4:" #(list-ref
+ :conc-name :predicate) fmt) #fn("n1200A3Y021Fe12223e2e12292e2e1247526q93535154@f0943S02127e12122e124945152e124935153@@02127e124935152e3:" #(λ
+ #fn(nconc) quote %struct% #fn(copy-list) foldr #fn("n2202105201PP:" #(#fn(sym) ":")) list) make-constructor)
+ sym-set-doc #fn(append) :doc-fmt #fn(nconc) begin def s equal? type-of quote and eq? aref =
+ length 1+ putprop constructor #fn(copy-list) map-int #fn("n1A<70F05251709205221938652943<0r20i2KM@30022872324q25e3e3953K0269523e2272896e223e3e3@30q292:25e22;2397360K@30E88Me37<2=85523O02>2?2@2886e22A2898e22Be6e2@G02C2397360K@30E88M24e4e4e4:" #(list-ref
#fn(sym) def s v v-supplied? unless type-error quote if not aref member :read-only error str "slot "
" in struct " " is :read-only" aset!)))) bcode:ctable #fn("n1200Ke3:" #(aref)) with-output-to #fn("z12021e1220e2e1e12315163:" #(#fn(nconc)
with-bindings *io-out* #fn(copy-list))) catch #fn("n22012122e123242522e2262722e22829e2e3262:22e20e3e42;22e22<22e2e4e3e3:" #(trycatch
--- a/src/system.sl
+++ b/src/system.sl
@@ -1148,19 +1148,20 @@
(arg-error "structs of type `vec` are always :named T"))
T)
named)
- name)]
+ (list name))]
; struct's predicate name
[is? (and predicate
(if (eq? predicate T)
(and named (sym name #\?)) ; FIXME(sigrid): need a "is set?" third arg
- (if (not named)
- (arg-error "predicate not possible unless the struct is :named T")
- predicate)))]
+ (if named
+ predicate
+ (arg-error "predicate not possible unless the struct is :named T"))))]
; what (type-of ...) should return if predicate is defined
[type-of-value (if isvec
(list 'struct name)
(list name '…))]
; constructor name and arguments
+ [constructor-default? (eq? constructor T)]
[constructor
(and constructor ; NIL means none to make at all
(or (and (atom? constructor) ; a single argument
@@ -1182,7 +1183,7 @@
(unless isvec
(list :type type))
(unless named
- (list :named named))
+ (cons :named named))
(unless (and constructor (eq? constructor T))
(list :constructor constructor))
(when conc-name
@@ -1192,6 +1193,16 @@
(list :predicate predicate))
(cons name slots))
tl)))
+ (def (make-constructor args)
+ `(λ ,args
+ ,(if isvec
+ `(,type '%struct%
+ ',name
+ ,@(foldr (λ (s z) (cons (sym ":" s) (cons s z)))
+ NIL
+ slots-car))
+ (if named `(list ',@named ,@slots-car)
+ `(list ,@slots-car)))))
(when docs
(sym-set-doc name (append docs (list (cons :doc-fmt fmt)))))
`(begin
@@ -1204,18 +1215,16 @@
[= (length s) ,(1+ num-slots)]))))
; constructor
,(when constructor
- (if isvec
- `(begin
- (def ,constructor
- (,type '%struct%
- ',name
- ,@(foldr (λ (s z) (cons (sym ":" s) (cons s z)))
- NIL
- slots-car)))
- (putprop ',name 'constructor ,(car constructor)))
- `(def ,constructor
- ,(if named `(list ',named ,@slots-car)
- `(list ,@slots-car)))))
+ `(def ,(car constructor) ,(make-constructor (cdr constructor))))
+
+ ; default constructor
+ ,(when isvec
+ `(putprop ',name
+ 'constructor
+ ,(if constructor-default?
+ (car constructor)
+ (make-constructor slots-kw))))
+
; accessor per slot
,@(map-int (λ (i) [let* {[opts (slot-opts (list-ref slots-kw i))]
[fld (list-ref slots-car i)]
--- a/test/defstruct.sl
+++ b/test/defstruct.sl
@@ -62,3 +62,32 @@
(assert (= (sln-c lx) 3))
(sl-a lx lx)
(assert (eq? (sl-a lx) lx))
+
+(defstruct ok :named T a)
+(assert-fail (macroexpand '(defstruct fail :named NIL a)))
+
+; custom constructor - renamed
+(defstruct scc :constructor scc_ a b (c 3))
+(assert (not (bound? 'make-scc)))
+(assert (bound? 'scc_))
+(def ccx (scc_ :a 1 :b 2))
+(assert (= (scc-a ccx) 1))
+(assert (= (scc-b ccx) 2))
+(assert (= (scc-c ccx) 3))
+(let ((b (buffer))) ; able to read the #S(...) form with a renamed constructor
+ (write ccx b)
+ (io-seek b 0)
+ (assert (equal? (read b) ccx)))
+
+; custom constructor - no keywords
+(defstruct scc2 :constructor (scc2 a b c) a b (c 3))
+(assert (not (bound? 'make-scc2)))
+(assert (bound? 'scc2))
+(def cc2x (scc2 1 2 4))
+(assert (= (scc2-a cc2x) 1))
+(assert (= (scc2-b cc2x) 2))
+(assert (= (scc2-c cc2x) 4))
+(let ((b (buffer))) ; able to read the #S(...) form with a custom constructor
+ (write cc2x b)
+ (io-seek b 0)
+ (assert (equal? (read b) cc2x)))