Skip to content

Commit 8e87d66

Browse files
authored
Merge pull request #39 from VirtusLab/better-match-types
Better match types
2 parents 6137bd5 + c047d70 commit 8e87d66

20 files changed

+410
-405
lines changed

src/main/ColumnOp.scala

Lines changed: 114 additions & 193 deletions
Large diffs are not rendered by default.

src/main/DataFrameBuilders.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import scala.quoted._
44
import org.apache.spark.sql
55
import org.apache.spark.sql.SparkSession
66
import org.virtuslab.iskra.DataFrame
7-
import org.virtuslab.iskra.types.{DataType, StructType, Encoder, StructEncoder, PrimitiveEncoder}
7+
import org.virtuslab.iskra.types.{DataType, Encoder, StructEncoder, PrimitiveEncoder}
88

99
object DataFrameBuilders:
1010
extension [A](seq: Seq[A])(using encoder: Encoder[A])

src/main/FrameSchema.scala

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,30 @@ import types.{DataType, Encoder, StructEncoder}
66
import MacroHelpers.TupleSubtype
77

88
object FrameSchema:
9-
type Merge[S1, S2] = S1 match
10-
case TupleSubtype[s1] => S2 match
11-
case TupleSubtype[s2] => Tuple.Concat[s1, s2]
12-
case _ => Tuple.Concat[s1, S2 *: EmptyTuple]
13-
case _ => S2 match
14-
case TupleSubtype[s2] => S1 *: s2
15-
case _ => S1 *: S2 *: EmptyTuple
9+
type AsTuple[A] = A match
10+
case Tuple => A
11+
case Any => A *: EmptyTuple
12+
13+
type FromTuple[T] = T match
14+
case h *: EmptyTuple => h
15+
case Tuple => T
16+
17+
type Merge[S1, S2] = (S1, S2) match
18+
case (Tuple, Tuple) =>
19+
Tuple.Concat[S1, S2]
20+
case (Any, Tuple) =>
21+
S1 *: S2
22+
case (Tuple, Any) =>
23+
Tuple.Append[S1, S2]
24+
case (Any, Any) =>
25+
(S1, S2)
1626

1727
type NullableLabeledDataType[T] = T match
18-
case label := tpe => label := DataType.Nullable[tpe]
28+
case label := tpe => label := DataType.AsNullable[tpe]
1929

2030
type NullableSchema[T] = T match
21-
case TupleSubtype[s] => Tuple.Map[s, NullableLabeledDataType]
22-
case _ => NullableLabeledDataType[T]
31+
case Tuple => Tuple.Map[T, NullableLabeledDataType]
32+
case Any => NullableLabeledDataType[T]
2333

2434
def reownType[Owner <: Name : Type](schema: Type[?])(using Quotes): Type[?] =
2535
schema match

src/main/JoinOnCondition.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package org.virtuslab.iskra
33
import scala.language.implicitConversions
44

55
import scala.quoted.*
6-
import org.virtuslab.iskra.types.BooleanOptType
6+
import org.virtuslab.iskra.types.BooleanOptLike
77

88
trait OnConditionJoiner[Join <: JoinType, Left, Right]
99

@@ -73,7 +73,7 @@ object JoinOnCondition:
7373
import quotes.reflect.*
7474

7575
'{ ${ condition }(using ${ joiningView }) } match
76-
case '{ $cond: Col[BooleanOptType] } =>
76+
case '{ $cond: Col[BooleanOptLike] } =>
7777
'{
7878
val joined = ${ join }.left.join(${ join }.right, ${ cond }.untyped, JoinType.typeName[T])
7979
StructDataFrame[JoinedSchema](joined)

src/main/MacroHelpers.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,3 @@ private[iskra] object MacroHelpers:
1111
Position(file, start, end)
1212

1313
type TupleSubtype[T <: Tuple] = T
14-
15-
type AsTuple[A] <: Tuple = A match
16-
case TupleSubtype[t] => t
17-
case _ => A *: EmptyTuple

src/main/SchemaView.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package org.virtuslab.iskra
33
import scala.quoted.*
44
import org.apache.spark.sql.functions.col
55
import types.DataType
6-
import MacroHelpers.AsTuple
6+
import MacroHelpers.TupleSubtype
77

88
inline def $(using view: SchemaView): view.type = view
99

@@ -79,7 +79,7 @@ object StructSchemaView:
7979
import quotes.reflect.*
8080
Type.of[DF] match
8181
case '[StructDataFrame[schema]] =>
82-
val schemaType = Type.of[AsTuple[schema]]
82+
val schemaType = Type.of[FrameSchema.AsTuple[schema]]
8383
val aliasViewsByName = frameAliasViewsByName(schemaType)
8484
val columns = unambiguousColumns(schemaType)
8585
val frameAliasNames = Expr(aliasViewsByName.map(_._1))

src/main/Select.scala

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,7 @@ object Select:
3737
case Some(collectColumns) =>
3838
collectColumns match
3939
case '{ $cc: CollectColumns[?] { type CollectedColumns = collectedColumns } } =>
40-
Type.of[collectedColumns] match
41-
case '[head *: EmptyTuple] =>
42-
'{
43-
val cols = ${ cc }.underlyingColumns(${ columns }(using ${ select }.view))
44-
StructDataFrame[head](${ select }.underlying.select(cols*))
45-
}
46-
40+
Type.of[FrameSchema.FromTuple[collectedColumns]] match
4741
case '[s] =>
4842
'{
4943
val cols = ${ cc }.underlyingColumns(${ columns }(using ${ select }.view))

src/main/StructDataFrame.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.virtuslab.iskra
33
import scala.quoted.*
44

55
import types.{DataType, Encoder, StructEncoder}
6+
import MacroHelpers.TupleSubtype
67

78

89
class StructDataFrame[Schema](val untyped: UntypedDataFrame) extends DataFrame
@@ -20,7 +21,13 @@ object StructDataFrame:
2021
Expr.summon[Encoder[A]] match
2122
case Some(encoder) => encoder match
2223
case '{ $enc: StructEncoder[A] { type StructSchema = structSchema } } =>
23-
Type.of[MacroHelpers.AsTuple[FrameSchema]] match
24+
val frameSchemaTuple = Type.of[FrameSchema] match
25+
case '[TupleSubtype[t]] =>
26+
Type.of[t]
27+
case '[t] =>
28+
Type.of[t *: EmptyTuple]
29+
30+
frameSchemaTuple match
2431
case '[`structSchema`] =>
2532
'{ ClassDataFrame[A](${ df }.untyped) }
2633
case _ =>

src/main/UntypedOps.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package org.virtuslab.iskra
22

33
import scala.quoted.*
4-
import types.{DataType, Encoder, StructType, StructEncoder}
4+
import types.{DataType, Encoder, struct, StructEncoder, StructNotNull}
55

66
object UntypedOps:
77
extension (untyped: UntypedColumn)
@@ -12,5 +12,5 @@ object UntypedOps:
1212

1313
private def typedDataFrameImpl[A : Type](df: Expr[UntypedDataFrame], encoder: Expr[StructEncoder[A]])(using Quotes) =
1414
encoder match
15-
case '{ ${e}: Encoder.Aux[tpe, StructType[t]] } =>
15+
case '{ ${e}: Encoder.Aux[tpe, StructNotNull[t]] } =>
1616
'{ ClassDataFrame[A](${ df }) }

src/main/When.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package org.virtuslab.iskra
22

33
import org.apache.spark.sql.{functions => f, Column => UntypedColumn}
4-
import org.virtuslab.iskra.types.{Coerce, DataType, BooleanOptType}
4+
import org.virtuslab.iskra.types.{Coerce, DataType, BooleanOptLike}
55

66
object When:
7-
class WhenColumn[T <: DataType](untyped: UntypedColumn) extends Col[DataType.Nullable[T]](untyped):
8-
def when[U <: DataType](condition: Col[BooleanOptType], value: Col[U])(using coerce: Coerce[T, U]): WhenColumn[coerce.Coerced] =
7+
class WhenColumn[T <: DataType](untyped: UntypedColumn) extends Col[DataType.AsNullable[T]](untyped):
8+
def when[U <: DataType](condition: Col[BooleanOptLike], value: Col[U])(using coerce: Coerce[T, U]): WhenColumn[coerce.Coerced] =
99
WhenColumn(this.untyped.when(condition.untyped, value.untyped))
1010
def otherwise[U <: DataType](value: Col[U])(using coerce: Coerce[T, U]): Col[coerce.Coerced] =
1111
Col(this.untyped.otherwise(value.untyped))
1212

13-
def when[T <: DataType](condition: Col[BooleanOptType], value: Col[T]): WhenColumn[T] =
13+
def when[T <: DataType](condition: Col[BooleanOptLike], value: Col[T]): WhenColumn[T] =
1414
WhenColumn(f.when(condition.untyped, value.untyped))

src/main/Where.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package org.virtuslab.iskra
22

33
import scala.quoted.*
4-
import org.virtuslab.iskra.types.BooleanOptType
4+
import org.virtuslab.iskra.types.BooleanOptLike
55

66
trait Where[Schema, View <: SchemaView]:
77
val view: View
@@ -36,7 +36,7 @@ object Where:
3636
import quotes.reflect.*
3737

3838
'{ ${ condition }(using ${ where }.view) } match
39-
case '{ $cond: Col[BooleanOptType] } =>
39+
case '{ $cond: Col[BooleanOptLike] } =>
4040
'{
4141
val filtered = ${ where }.underlying.where(${ cond }.untyped)
4242
StructDataFrame[Schema](filtered)

src/main/api/api.scala

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,46 @@ package api
33

44
export DataFrameBuilders.toDF
55
export types.{
6-
DataType,
7-
BooleanType,
8-
BooleanOptType,
9-
StringType,
10-
StringOptType,
11-
ByteType,
12-
ByteOptType,
13-
ShortType,
14-
ShortOptType,
15-
IntegerType,
16-
IntegerOptType,
17-
LongType,
18-
LongOptType,
19-
FloatType,
20-
FloatOptType,
21-
DoubleType,
22-
DoubleOptType,
23-
StructType,
24-
StructOptType
6+
boolean,
7+
boolean_?,
8+
BooleanNotNull,
9+
BooleanOrNull,
10+
string,
11+
string_?,
12+
StringNotNull,
13+
StringOrNull,
14+
byte,
15+
byte_?,
16+
ByteNotNull,
17+
ByteOrNull,
18+
short,
19+
short_?,
20+
ShortNotNull,
21+
ShortOrNull,
22+
int,
23+
int_?,
24+
IntNotNull,
25+
IntOrNull,
26+
long,
27+
long_?,
28+
LongNotNull,
29+
LongOrNull,
30+
float,
31+
float_?,
32+
FloatNotNull,
33+
FloatOrNull,
34+
double,
35+
double_?,
36+
DoubleNotNull,
37+
DoubleOrNull,
38+
struct,
39+
struct_?,
40+
StructNotNull,
41+
StructOrNull
2542
}
2643
export UntypedOps.typed
2744
export org.virtuslab.iskra.$
28-
export org.virtuslab.iskra.{Column, Columns, DataFrame, ClassDataFrame, NamedColumns, StructDataFrame, UntypedColumn, UntypedDataFrame, :=, /}
45+
export org.virtuslab.iskra.{Column, Columns, Col, DataFrame, ClassDataFrame, NamedColumns, StructDataFrame, UntypedColumn, UntypedDataFrame, :=, /}
2946

3047
object functions:
3148
export org.virtuslab.iskra.functions.{lit, when}

src/main/functions/aggregates.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@ import org.virtuslab.iskra.Agg
55
import org.virtuslab.iskra.Col
66
import org.virtuslab.iskra.UntypedOps.typed
77
import org.virtuslab.iskra.types.*
8-
import org.virtuslab.iskra.types.DataType.{NumericOptType, Nullable}
8+
import org.virtuslab.iskra.types.DataType.AsNullable
99

1010
class Sum[A <: Agg](val agg: A):
11-
def apply[T <: NumericOptType](column: agg.View ?=> Col[T]): Col[Nullable[T]] =
11+
def apply[T <: DoubleOptLike](column: agg.View ?=> Col[T]): Col[AsNullable[T]] =
1212
sql.functions.sum(column(using agg.view).untyped).typed
1313

1414
class Max[A <: Agg](val agg: A):
15-
def apply[T <: NumericOptType](column: agg.View ?=> Col[T]): Col[Nullable[T]] =
15+
def apply[T <: DoubleOptLike](column: agg.View ?=> Col[T]): Col[AsNullable[T]] =
1616
sql.functions.max(column(using agg.view).untyped).typed
1717

1818
class Min[A <: Agg](val agg: A):
19-
def apply[T <: NumericOptType](column: agg.View ?=> Col[T]): Col[Nullable[T]] =
19+
def apply[T <: DoubleOptLike](column: agg.View ?=> Col[T]): Col[AsNullable[T]] =
2020
sql.functions.min(column(using agg.view).untyped).typed
2121

2222
class Avg[A <: Agg](val agg: A):
23-
def apply(column: agg.View ?=> Col[NumericOptType]): Col[DoubleOptType] =
23+
def apply(column: agg.View ?=> Col[DoubleOptLike]): Col[DoubleOrNull] =
2424
sql.functions.avg(column(using agg.view).untyped).typed
2525

2626
object Aggregates:

src/main/types/Coerce.scala

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
package org.virtuslab.iskra
22
package types
33

4-
import DataType.{CommonNumericNonNullableType, CommonNumericNullableType, NumericOptType, NumericType}
5-
6-
trait Coerce[-A <: DataType, -B <: DataType]:
4+
trait Coerce[A <: DataType, B <: DataType]:
75
type Coerced <: DataType
86

9-
object Coerce:
10-
given sameType[A <: DataType]: Coerce[A, A] with
7+
object Coerce extends CoerceLowPrio:
8+
given sameType[A <: FinalDataType]: Coerce[A, A] with
9+
override type Coerced = A
10+
11+
given nullableFirst[A <: FinalDataType & Nullable, B <: FinalDataType & NonNullable](using A <:< NullableOf[B]): Coerce[A, B] with
1112
override type Coerced = A
1213

13-
given nullable[A <: NumericOptType, B <: NumericOptType]: Coerce[A, B] with
14-
override type Coerced = CommonNumericNullableType[A, B]
14+
given nullableSecond[A <: FinalDataType & NonNullable, B <: FinalDataType & Nullable](using A <:< NonNullableOf[B]): Coerce[A, B] with
15+
override type Coerced = B
1516

16-
given nonNullable[A <: NumericType, B <: NumericType]: Coerce[A, B] with
17-
override type Coerced = CommonNumericNonNullableType[A, B]
17+
trait CoerceLowPrio:
18+
given numeric[A <: FinalDataType & DoubleOptLike, B <: FinalDataType & DoubleOptLike]: (Coerce[A, B] { type Coerced = CommonNumericType[A, B] }) =
19+
new Coerce[A, B]:
20+
override type Coerced = CommonNumericType[A, B]

0 commit comments

Comments
 (0)