11package scala
22import compiletime .ops .boolean .*
3+ import compiletime .ops .int .*
4+ import scala .annotation .experimental
5+ import scala .annotation .implicitNotFound
36
47object NamedTuple :
58
@@ -25,10 +28,10 @@ object NamedTuple:
2528 extension [V <: Tuple ](x : V )
2629 inline def withNames [N <: Tuple ]: NamedTuple [N , V ] = x
2730
28- import NamedTupleDecomposition .{Names , DropNames }
31+ import NamedTupleDecomposition .{Names , DropNames , Decompose }
2932 export NamedTupleDecomposition .{
30- Names , DropNames ,
31- apply , size , init , head , last , tail , take , drop , splitAt , ++ , map , reverse , zip , toList , toArray , toIArray
33+ Names , DropNames , Decompose ,
34+ apply , size , init , head , last , tail , take , drop , splitAt , ++ , copyFrom , map , reverse , zip , toList , toArray , toIArray
3235 }
3336
3437 extension [N <: Tuple , V <: Tuple ](x : NamedTuple [N , V ])
@@ -116,6 +119,14 @@ object NamedTuple:
116119 case Names [Y ] =>
117120 NamedTuple [Names [X ], Tuple .Zip [DropNames [X ], DropNames [Y ]]]
118121
122+ @ experimental
123+ type Copy [X <: AnyNamedTuple , Y <: AnyNamedTuple ] = (Decompose [X ], Decompose [Y ]) match
124+ case ((nx, vx), (ny, vy)) =>
125+ NamedTuple [
126+ nx,
127+ NamedTupleDecomposition .Copy0 [nx, vx, ny, vy, EmptyTuple ]
128+ ]
129+
119130 /** A type specially treated by the compiler to represent all fields of a
120131 * class argument `T` as a named tuple. Or, if `T` is already a named tuple,
121132 * `From[T]` is the same as `T`.
@@ -174,6 +185,18 @@ object NamedTupleDecomposition:
174185 : Concat [NamedTuple [N , V ], NamedTuple [N2 , V2 ]]
175186 = x.toTuple ++ that.toTuple
176187
188+ /** The named tuple consisting of all elements of this tuple, with fields replaced by those from `that`.
189+ * The field names of the update tuple of updates must all be present in this tuple, but not all fields are required.
190+ */
191+ @ experimental
192+ inline def copyFrom [N2 <: Tuple , V2 <: Tuple ](that : NamedTuple [N2 , V2 ])(using Tuple .ContainsAll [N2 , N ] =:= true )
193+ : Copy [NamedTuple [N , V ], NamedTuple [N2 , V2 ]]
194+ = scala.runtime.Tuples .copy(
195+ self = x.toTuple,
196+ that = that.toTuple,
197+ indices = compiletime.constValueTuple[Tuple .Indices [N , N2 ]]
198+ ).asInstanceOf [Copy [NamedTuple [N , V ], NamedTuple [N2 , V2 ]]]
199+
177200 /** The named tuple consisting of all element values of this tuple mapped by
178201 * the polymorphic mapping function `f`. The names of elements are preserved.
179202 * If `x = (n1 = v1, ..., ni = vi)` then `x.map(f) = `(n1 = f(v1), ..., ni = f(vi))`.
@@ -205,10 +228,29 @@ object NamedTupleDecomposition:
205228
206229 end extension
207230
231+ @ experimental
232+ type LookupName [X , N <: Tuple , V <: Tuple ] <: Option [Any ] =
233+ (N , V ) match
234+ case (X *: _, v *: _) => Some [v]
235+ case (_ *: ns, _ *: vs) => LookupName [X , ns, vs]
236+ case (EmptyTuple , EmptyTuple ) => None .type
237+
238+ @ experimental
239+ type Copy0 [Nx <: Tuple , Vx <: Tuple , Ny <: Tuple , Vy <: Tuple , Acc <: Tuple ] <: Tuple =
240+ (Nx , Vx ) match
241+ case (nx *: nxs, vx *: vxs) => LookupName [nx, Ny , Vy ] match
242+ case Some [vy] => Copy0 [nxs, vxs, Ny , Vy , vy *: Acc ]
243+ case _ => Copy0 [nxs, vxs, Ny , Vy , vx *: Acc ]
244+ case (EmptyTuple , EmptyTuple ) => Tuple .Reverse [Acc ]
245+
208246 /** The names of a named tuple, represented as a tuple of literal string values. */
209247 type Names [X <: AnyNamedTuple ] <: Tuple = X match
210248 case NamedTuple [n, _] => n
211249
212250 /** The value types of a named tuple represented as a regular tuple. */
213251 type DropNames [NT <: AnyNamedTuple ] <: Tuple = NT match
214252 case NamedTuple [_, x] => x
253+
254+ @ experimental
255+ type Decompose [NT <: AnyNamedTuple ] <: Tuple = NT match
256+ case NamedTuple [n, x] => (n, x)
0 commit comments