Skip to content

Commit d2e000c

Browse files
committed
move NamedTuple methods to separate scope. re-export
1 parent 01b404f commit d2e000c

File tree

2 files changed

+238
-79
lines changed

2 files changed

+238
-79
lines changed

library/src/scala/NamedTuple.scala

+84-79
Original file line numberDiff line numberDiff line change
@@ -28,100 +28,26 @@ object NamedTuple:
2828
extension [V <: Tuple](x: V)
2929
inline def withNames[N <: Tuple]: NamedTuple[N, V] = x
3030

31-
export NamedTupleDecomposition.{Names, DropNames}
31+
export NamedTupleDecomposition.{
32+
Names, DropNames,
33+
apply, size, init, last, tail, take, drop, splitAt, ++, map, reverse, zip, toList, toArray, toIArray
34+
}
3235

3336
extension [N <: Tuple, V <: Tuple](x: NamedTuple[N, V])
3437

3538
/** The underlying tuple without the names */
3639
inline def toTuple: V = x
3740

38-
/** The number of elements in this tuple */
39-
inline def size: Tuple.Size[V] = toTuple.size
40-
4141
// This intentionally works for empty named tuples as well. I think NonEmptyTuple is a dead end
4242
// and should be reverted, just like NonEmptyList is also appealing at first, but a bad idea
4343
// in the end.
4444

45-
/** The value (without the name) at index `n` of this tuple */
46-
inline def apply(n: Int): Tuple.Elem[V, n.type] =
47-
inline toTuple match
48-
case tup: NonEmptyTuple => tup(n).asInstanceOf[Tuple.Elem[V, n.type]]
49-
case tup => tup.productElement(n).asInstanceOf[Tuple.Elem[V, n.type]]
50-
5145
/** The first element value of this tuple */
52-
inline def head: Tuple.Elem[V, 0] = apply(0)
53-
54-
/** The tuple consisting of all elements of this tuple except the first one */
55-
inline def tail: NamedTuple[Tuple.Tail[N], Tuple.Tail[V]] =
56-
toTuple.drop(1).asInstanceOf[NamedTuple[Tuple.Tail[N], Tuple.Tail[V]]]
57-
58-
/** The last element value of this tuple */
59-
inline def last: Tuple.Last[V] = apply(size - 1).asInstanceOf[Tuple.Last[V]]
60-
61-
/** The tuple consisting of all elements of this tuple except the last one */
62-
inline def init: NamedTuple[Tuple.Init[N], Tuple.Init[V]] =
63-
toTuple.take(size - 1).asInstanceOf[NamedTuple[Tuple.Init[N], Tuple.Init[V]]]
64-
65-
/** The tuple consisting of the first `n` elements of this tuple, or all
66-
* elements if `n` exceeds `size`.
67-
*/
68-
inline def take(n: Int): NamedTuple[Tuple.Take[N, n.type], Tuple.Take[V, n.type]] =
69-
toTuple.take(n)
70-
71-
/** The tuple consisting of all elements of this tuple except the first `n` ones,
72-
* or no elements if `n` exceeds `size`.
73-
*/
74-
inline def drop(n: Int): NamedTuple[Tuple.Drop[N, n.type], Tuple.Drop[V, n.type]] =
75-
toTuple.drop(n)
76-
77-
/** The tuple `(x.take(n), x.drop(n))` */
78-
inline def splitAt(n: Int):
79-
(NamedTuple[Tuple.Take[N, n.type], Tuple.Take[V, n.type]],
80-
NamedTuple[Tuple.Drop[N, n.type], Tuple.Drop[V, n.type]]) =
81-
// would be nice if this could have type `Split[NamedTuple[N, V]]` instead, but
82-
// we get a type error then. Similar for other methods here.
83-
toTuple.splitAt(n)
84-
85-
/** The tuple consisting of all elements of this tuple followed by all elements
86-
* of tuple `that`. The names of the two tuples must be disjoint.
87-
*/
88-
inline def ++ [N2 <: Tuple, V2 <: Tuple](that: NamedTuple[N2, V2])(using Tuple.Disjoint[N, N2] =:= true)
89-
: NamedTuple[Tuple.Concat[N, N2], Tuple.Concat[V, V2]]
90-
= toTuple ++ that.toTuple
46+
inline def head: Tuple.Elem[V, 0] = x.apply(0)
9147

9248
// inline def :* [L] (x: L): NamedTuple[Append[N, ???], Append[V, L] = ???
9349
// inline def *: [H] (x: H): NamedTuple[??? *: N], H *: V] = ???
9450

95-
/** The named tuple consisting of all element values of this tuple mapped by
96-
* the polymorphic mapping function `f`. The names of elements are preserved.
97-
* If `x = (n1 = v1, ..., ni = vi)` then `x.map(f) = `(n1 = f(v1), ..., ni = f(vi))`.
98-
*/
99-
inline def map[F[_]](f: [t] => t => F[t]): NamedTuple[N, Tuple.Map[V, F]] =
100-
toTuple.map(f).asInstanceOf[NamedTuple[N, Tuple.Map[V, F]]]
101-
102-
/** The named tuple consisting of all elements of this tuple in reverse */
103-
inline def reverse: NamedTuple[Tuple.Reverse[N], Tuple.Reverse[V]] =
104-
toTuple.reverse
105-
106-
/** The named tuple consisting of all elements values of this tuple zipped
107-
* with corresponding element values in named tuple `that`.
108-
* If the two tuples have different sizes,
109-
* the extra elements of the larger tuple will be disregarded.
110-
* The names of `x` and `that` at the same index must be the same.
111-
* The result tuple keeps the same names as the operand tuples.
112-
*/
113-
inline def zip[V2 <: Tuple](that: NamedTuple[N, V2]): NamedTuple[N, Tuple.Zip[V, V2]] =
114-
toTuple.zip(that.toTuple)
115-
116-
/** A list consisting of all element values */
117-
inline def toList: List[Tuple.Union[V]] = toTuple.toList.asInstanceOf[List[Tuple.Union[V]]]
118-
119-
/** An array consisting of all element values */
120-
inline def toArray: Array[Object] = toTuple.toArray
121-
122-
/** An immutable array consisting of all element values */
123-
inline def toIArray: IArray[Object] = toTuple.toIArray
124-
12551
end extension
12652

12753
/** The size of a named tuple, represented as a literal constant subtype of Int */
@@ -212,6 +138,85 @@ end NamedTuple
212138
@experimental
213139
object NamedTupleDecomposition:
214140
import NamedTuple.*
141+
extension [N <: Tuple, V <: Tuple](x: NamedTuple[N, V])
142+
/** The value (without the name) at index `n` of this tuple */
143+
inline def apply(n: Int): Tuple.Elem[V, n.type] =
144+
inline x.toTuple match
145+
case tup: NonEmptyTuple => tup(n).asInstanceOf[Tuple.Elem[V, n.type]]
146+
case tup => tup.productElement(n).asInstanceOf[Tuple.Elem[V, n.type]]
147+
148+
/** The number of elements in this tuple */
149+
inline def size: Tuple.Size[V] = x.toTuple.size
150+
151+
/** The last element value of this tuple */
152+
inline def last: Tuple.Last[V] = apply(size - 1).asInstanceOf[Tuple.Last[V]]
153+
154+
/** The tuple consisting of all elements of this tuple except the last one */
155+
inline def init: NamedTuple[Tuple.Init[N], Tuple.Init[V]] =
156+
x.toTuple.take(size - 1).asInstanceOf[NamedTuple[Tuple.Init[N], Tuple.Init[V]]]
157+
158+
/** The tuple consisting of all elements of this tuple except the first one */
159+
inline def tail: NamedTuple[Tuple.Tail[N], Tuple.Tail[V]] =
160+
x.toTuple.drop(1).asInstanceOf[NamedTuple[Tuple.Tail[N], Tuple.Tail[V]]]
161+
162+
/** The tuple consisting of the first `n` elements of this tuple, or all
163+
* elements if `n` exceeds `size`.
164+
*/
165+
inline def take(n: Int): NamedTuple[Tuple.Take[N, n.type], Tuple.Take[V, n.type]] =
166+
x.toTuple.take(n)
167+
168+
/** The tuple consisting of all elements of this tuple except the first `n` ones,
169+
* or no elements if `n` exceeds `size`.
170+
*/
171+
inline def drop(n: Int): NamedTuple[Tuple.Drop[N, n.type], Tuple.Drop[V, n.type]] =
172+
x.toTuple.drop(n)
173+
174+
/** The tuple `(x.take(n), x.drop(n))` */
175+
inline def splitAt(n: Int):
176+
(NamedTuple[Tuple.Take[N, n.type], Tuple.Take[V, n.type]],
177+
NamedTuple[Tuple.Drop[N, n.type], Tuple.Drop[V, n.type]]) =
178+
// would be nice if this could have type `Split[NamedTuple[N, V]]` instead, but
179+
// we get a type error then. Similar for other methods here.
180+
x.toTuple.splitAt(n)
181+
182+
/** The tuple consisting of all elements of this tuple followed by all elements
183+
* of tuple `that`. The names of the two tuples must be disjoint.
184+
*/
185+
inline def ++ [N2 <: Tuple, V2 <: Tuple](that: NamedTuple[N2, V2])(using Tuple.Disjoint[N, N2] =:= true)
186+
: NamedTuple[Tuple.Concat[N, N2], Tuple.Concat[V, V2]]
187+
= x.toTuple ++ that.toTuple
188+
189+
/** The named tuple consisting of all element values of this tuple mapped by
190+
* the polymorphic mapping function `f`. The names of elements are preserved.
191+
* If `x = (n1 = v1, ..., ni = vi)` then `x.map(f) = `(n1 = f(v1), ..., ni = f(vi))`.
192+
*/
193+
inline def map[F[_]](f: [t] => t => F[t]): NamedTuple[N, Tuple.Map[V, F]] =
194+
x.toTuple.map(f)
195+
196+
/** The named tuple consisting of all elements of this tuple in reverse */
197+
inline def reverse: NamedTuple[Tuple.Reverse[N], Tuple.Reverse[V]] =
198+
x.toTuple.reverse
199+
200+
/** The named tuple consisting of all elements values of this tuple zipped
201+
* with corresponding element values in named tuple `that`.
202+
* If the two tuples have different sizes,
203+
* the extra elements of the larger tuple will be disregarded.
204+
* The names of `x` and `that` at the same index must be the same.
205+
* The result tuple keeps the same names as the operand tuples.
206+
*/
207+
inline def zip[V2 <: Tuple](that: NamedTuple[N, V2]): NamedTuple[N, Tuple.Zip[V, V2]] =
208+
x.toTuple.zip(that.toTuple)
209+
210+
/** A list consisting of all element values */
211+
inline def toList: List[Tuple.Union[V]] = x.toTuple.toList
212+
213+
/** An array consisting of all element values */
214+
inline def toArray: Array[Object] = x.toTuple.toArray
215+
216+
/** An immutable array consisting of all element values */
217+
inline def toIArray: IArray[Object] = x.toTuple.toIArray
218+
219+
end extension
215220

216221
/** The names of a named tuple, represented as a tuple of literal string values. */
217222
type Names[X <: AnyNamedTuple] <: Tuple = X match
+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import scala.language.experimental.namedTuples
2+
3+
object Test:
4+
// original code from issue https://github.com/scala/scala3/issues/20427
5+
type NT = NamedTuple.Concat[(hi: Int), (bla: String)]
6+
def foo(x: NT) =
7+
x.hi // error
8+
val y: (hi: Int, bla: String) = x
9+
y.hi // ok
10+
11+
// SELECTOR (seduar to apply)
12+
def foo1(x: NT) =
13+
val res1 = x.hi // error
14+
summon[res1.type <:< Int]
15+
val y: (hi: Int, bla: String) = x
16+
val res2 = y.hi // ok
17+
summon[res2.type <:< Int]
18+
19+
// toTuple
20+
def foo2(x: NT) =
21+
val res1 = x.toTuple
22+
summon[res1.type <:< (Int, String)]
23+
val y: (hi: Int, bla: String) = x
24+
val res2 = y.toTuple
25+
summon[res2.type <:< (Int, String)]
26+
27+
// apply
28+
def foo3(x: NT) =
29+
val res1 = x.apply(1)
30+
summon[res1.type <:< String]
31+
val y: (hi: Int, bla: String) = x
32+
val res2 = y.apply(1)
33+
summon[res2.type <:< String]
34+
35+
// size
36+
def foo4(x: NT) =
37+
class Box:
38+
final val res1 = x.size // final val constrains to a singleton type
39+
summon[res1.type <:< 2]
40+
val y: (hi: Int, bla: String) = x
41+
final val res2 = y.size // final val constrains to a singleton type
42+
summon[res2.type <:< 2]
43+
44+
// head
45+
def foo5(x: NT) =
46+
val res1 = x.head
47+
summon[res1.type <:< Int]
48+
val y: (hi: Int, bla: String) = x
49+
val res2 = y.head
50+
summon[res2.type <:< Int]
51+
52+
// last
53+
def foo6(x: NT) =
54+
val res1 = x.last
55+
summon[res1.type <:< String]
56+
val y: (hi: Int, bla: String) = x
57+
val res2 = y.last
58+
summon[res2.type <:< String]
59+
60+
// init
61+
def foo7(x: NT) =
62+
val res1 = x.init
63+
summon[res1.type <:< (hi: Int)]
64+
val y: (hi: Int, bla: String) = x
65+
val res2 = y.init
66+
summon[res2.type <:< (hi: Int)]
67+
68+
// tail
69+
def foo8(x: NT) =
70+
val res1 = x.tail
71+
summon[res1.type <:< (bla: String)]
72+
val y: (hi: Int, bla: String) = x
73+
val res2 = y.tail
74+
summon[res2.type <:< (bla: String)]
75+
76+
// take
77+
def foo9(x: NT) =
78+
val res1 = x.take(1)
79+
summon[res1.type <:< (hi: Int)]
80+
val y: (hi: Int, bla: String) = x
81+
val res2 = y.take(1)
82+
summon[res2.type <:< (hi: Int)]
83+
84+
// drop
85+
def foo10(x: NT) =
86+
val res1 = x.drop(1)
87+
summon[res1.type <:< (bla: String)]
88+
val y: (hi: Int, bla: String) = x
89+
val res2 = y.drop(1)
90+
summon[res2.type <:< (bla: String)]
91+
92+
// splitAt
93+
def foo11(x: NT) =
94+
val res1 = x.splitAt(1)
95+
summon[res1.type <:< ((hi: Int), (bla: String))]
96+
val y: (hi: Int, bla: String) = x
97+
val res2 = y.splitAt(1)
98+
summon[res2.type <:< ((hi: Int), (bla: String))]
99+
100+
// ++
101+
def foo12(x: NT) =
102+
val res1 = x ++ (baz = 23)
103+
summon[res1.type <:< (hi: Int, bla: String, baz: Int)]
104+
val y: (hi: Int, bla: String) = x
105+
val res2 = y ++ (baz = 23)
106+
summon[res2.type <:< (hi: Int, bla: String, baz: Int)]
107+
108+
// map
109+
def foo13(x: NT) =
110+
val res1 = x.map([T] => (t: T) => Option(t))
111+
summon[res1.type <:< (hi: Option[Int], bla: Option[String])]
112+
val y: (hi: Int, bla: String) = x
113+
val res2 = y.map([T] => (t: T) => Option(t))
114+
summon[res2.type <:< (hi: Option[Int], bla: Option[String])]
115+
116+
// reverse
117+
def foo14(x: NT) =
118+
val res1 = x.reverse
119+
summon[res1.type <:< (bla: String, hi: Int)]
120+
val y: (hi: Int, bla: String) = x
121+
val res2 = y.reverse
122+
summon[res2.type <:< (bla: String, hi: Int)]
123+
124+
// zip
125+
def foo15(x: NT) =
126+
val res1 = x.zip((hi = "xyz", bla = true))
127+
summon[res1.type <:< (hi: (Int, String), bla: (String, Boolean))]
128+
val y: (hi: Int, bla: String) = x
129+
val res2 = y.zip((hi = "xyz", bla = true))
130+
summon[res2.type <:< (hi: (Int, String), bla: (String, Boolean))]
131+
132+
// toList
133+
def foo16(x: NT) =
134+
val res1 = x.toList
135+
summon[res1.type <:< List[Tuple.Union[(Int, String)]]]
136+
val y: (hi: Int, bla: String) = x
137+
val res2 = y.toList
138+
summon[res2.type <:< List[Tuple.Union[(Int, String)]]]
139+
140+
// toArray
141+
def foo17(x: NT) =
142+
val res1 = x.toArray
143+
summon[res1.type <:< Array[Object]]
144+
val y: (hi: Int, bla: String) = x
145+
val res2 = y.toArray
146+
summon[res2.type <:< Array[Object]]
147+
148+
// toIArray
149+
def foo18(x: NT) =
150+
val res1 = x.toIArray
151+
summon[res1.type <:< IArray[Object]]
152+
val y: (hi: Int, bla: String) = x
153+
val res2 = y.toIArray
154+
summon[res2.type <:< IArray[Object]]

0 commit comments

Comments
 (0)