Skip to content

Commit 9148b83

Browse files
committed
_replace for records
1 parent 38ce2d7 commit 9148b83

File tree

1 file changed

+44
-1
lines changed

1 file changed

+44
-1
lines changed

kdrag/notation.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,49 @@ def datatype_call(self, *args):
229229
smt.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+
232275
def 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

438481
def 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

Comments
 (0)