@@ -229,6 +229,49 @@ def datatype_call(self, *args):
229229smt .DatatypeSortRef .__call__ = datatype_call
230230
231231
232+ def datatype_replace (self , ** kwargs ):
233+ """
234+ Like NamedTuple, you can replace fields of a record datatype.
235+
236+ >>> Point = kd.Record("Point", ("x", smt.RealSort()), ("y", smt.RealSort()), admit=True)
237+ >>> Point(0,1)._replace(x=3, y=10)
238+ Point(3, 10)
239+ >>> p = smt.Const("p", Point)
240+ >>> q = p._replace(y=10)
241+ >>> q
242+ Point(x(p), 10)
243+ >>> q._replace(x=1)
244+ Point(1, 10)
245+ """
246+ sort = self .sort ()
247+
248+ if sort .num_constructors () != 1 :
249+ raise TypeError (
250+ "`_replace` is not supported on datatypes with multiple constructors"
251+ )
252+
253+ cons = sort .constructor (0 )
254+ accs = [sort .accessor (0 , i ) for i in range (cons .arity ())]
255+ names = {acc .name () for acc in accs } # Use a set for quick lookup
256+
257+ invalid_fields = kwargs .keys () - names
258+ if invalid_fields :
259+ raise ValueError (
260+ f"Constructor `{ cons .name ()} ` does not have fields: { ', ' .join (invalid_fields )} "
261+ )
262+
263+ defaults = (
264+ self .children () if smt .is_constructor (self ) else [acc (self ) for acc in accs ]
265+ )
266+
267+ fields = [kwargs .get (acc .name (), default ) for acc , default in zip (accs , defaults )]
268+
269+ return cons (* fields )
270+
271+
272+ smt .DatatypeRef ._replace = datatype_replace
273+
274+
232275def datatype_iter (self ):
233276 return (self .constructor (i ) for i in range (self .num_constructors ()))
234277
@@ -436,7 +479,7 @@ def cond(*cases, default=None) -> smt.ExprRef:
436479
437480
438481def InductiveRel (name : str , * param_sorts , admit = False ) -> smt .DatatypeSortRef :
439- """Define an inductive type of evidence alongside and a relation the recurses on that evidence
482+ """Define an inductive type of evidence and a relation the recurses on that evidence
440483
441484 >>> Even = InductiveRel("Even", smt.IntSort(), admit=True)
442485 >>> Even.declare("Ev_Z", pred=lambda x: x == 0)
0 commit comments