Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map encoder with Value class #567

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ lazy val dataset = project

// TODO: Remove have version bump
Seq(
imt("frameless.TypedEncoder.mapEncoder"),
imt("frameless.TypedEncoder.arrayEncoder"),
imt("frameless.RecordEncoderFields.deriveRecordCons"),
imt("frameless.RecordEncoderFields.deriveRecordLast"),
mc("frameless.functions.FramelessLit"),
Expand Down
112 changes: 79 additions & 33 deletions dataset/src/main/scala/frameless/RecordEncoder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,11 @@ class RecordEncoder[F, G <: HList, H <: HList]
}

final class RecordFieldEncoder[T](
val encoder: TypedEncoder[T]) extends Serializable
val encoder: TypedEncoder[T],
private[frameless] val jvmRepr: DataType,
private[frameless] val fromCatalyst: Expression => Expression,
private[frameless] val toCatalyst: Expression => Expression
) extends Serializable

object RecordFieldEncoder extends RecordFieldEncoderLowPriority {

Expand All @@ -197,60 +201,102 @@ object RecordFieldEncoder extends RecordFieldEncoderLowPriority {
i4: IsHCons.Aux[KS, K, HNil],
i5: TypedEncoder[V],
i6: ClassTag[F]
): RecordFieldEncoder[Option[F]] = RecordFieldEncoder[Option[F]](new TypedEncoder[Option[F]] {
val nullable = true

val jvmRepr = ObjectType(classOf[Option[F]])
): RecordFieldEncoder[Option[F]] = {
val fieldName = i4.head(i3()).name
val innerJvmRepr = ObjectType(i6.runtimeClass)

@inline def catalystRepr: DataType = i5.catalystRepr
val catalyst: Expression => Expression = { path =>
val value = UnwrapOption(innerJvmRepr, path)
val javaValue = Invoke(value, fieldName, i5.jvmRepr, Nil)

val innerJvmRepr = ObjectType(i6.runtimeClass)
i5.toCatalyst(javaValue)
}

def fromCatalyst(path: Expression): Expression = {
val fromCatalyst: Expression => Expression = { path =>
val javaValue = i5.fromCatalyst(path)
val value = NewInstance(i6.runtimeClass, Seq(javaValue), innerJvmRepr)

WrapOption(value, innerJvmRepr)
}

@inline def toCatalyst(path: Expression): Expression = {
val value = UnwrapOption(innerJvmRepr, path)
val jvmr = ObjectType(classOf[Option[F]])

val fieldName = i4.head(i3()).name
val javaValue = Invoke(value, fieldName, i5.jvmRepr, Nil)
new RecordFieldEncoder[Option[F]](
encoder = new TypedEncoder[Option[F]] {
val nullable = true

i5.toCatalyst(javaValue)
}
})
val jvmRepr = jvmr

@inline def catalystRepr: DataType = i5.catalystRepr

def fromCatalyst(path: Expression): Expression = {
val javaValue = i5.fromCatalyst(path)
val value = NewInstance(
i6.runtimeClass, Seq(javaValue), innerJvmRepr)

WrapOption(value, innerJvmRepr)
}

def toCatalyst(path: Expression): Expression = catalyst(path)

override def toString: String = s"RecordFieldEncoder.optionValueClass[${i6.runtimeClass.getName}]('${fieldName}', $i5)"
},
jvmRepr = jvmr,
fromCatalyst = fromCatalyst,
toCatalyst = catalyst
)
}

/**
* @tparam F the value class
* @tparam G the single field of the value class
* @tparam H the single field of the value class (with guarantee it's not a `Unit` value)
* @tparam V the inner value type
*/
implicit def valueClass[F : IsValueClass, G <: ::[_, HNil], H <: ::[_, HNil], V]
implicit def valueClass[F : IsValueClass, G <: ::[_, HNil], H <: ::[_ <: FieldType[_ <: Symbol, _], HNil], K <: Symbol, V, KS <: ::[_ <: Symbol, HNil]]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also capture keys evidence so it can be used to wrap/unwrap in the map/array/collection encoder of Value class

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cchantep thanks for clarifying it.

(implicit
i0: LabelledGeneric.Aux[F, G],
i1: DropUnitValues.Aux[G, H],
i2: IsHCons.Aux[H, _ <: FieldType[_, V], HNil],
i3: TypedEncoder[V],
i4: ClassTag[F]
): RecordFieldEncoder[F] = RecordFieldEncoder[F](new TypedEncoder[F] {
def nullable = i3.nullable

def jvmRepr = i3.jvmRepr

def catalystRepr: DataType = i3.catalystRepr

def fromCatalyst(path: Expression): Expression =
i3.fromCatalyst(path)

@inline def toCatalyst(path: Expression): Expression =
i3.toCatalyst(path)
})
i2: IsHCons.Aux[H, _ <: FieldType[K, V], HNil],
i3: Keys.Aux[H, KS],
i4: IsHCons.Aux[KS, K, HNil],
i5: TypedEncoder[V],
i6: ClassTag[F]
): RecordFieldEncoder[F] = {
val cls = i6.runtimeClass
val jvmr = i5.jvmRepr
val fieldName = i4.head(i3()).name

new RecordFieldEncoder[F](
encoder = new TypedEncoder[F] {
def nullable = i5.nullable

def jvmRepr = jvmr

def catalystRepr: DataType = i5.catalystRepr

def fromCatalyst(path: Expression): Expression =
i5.fromCatalyst(path)

@inline def toCatalyst(path: Expression): Expression =
i5.toCatalyst(path)

override def toString: String = s"RecordFieldEncoder.valueClass[${cls.getName}]('${fieldName}', ${i5})"
},
jvmRepr = FramelessInternals.objectTypeFor[F],
fromCatalyst = { expr: Expression =>
NewInstance(
i6.runtimeClass,
i5.fromCatalyst(expr) :: Nil,
ObjectType(i6.runtimeClass))
},
toCatalyst = { expr: Expression =>
i5.toCatalyst(Invoke(expr, fieldName, jvmr))
}
)
}
}

private[frameless] sealed trait RecordFieldEncoderLowPriority {
implicit def apply[T](implicit e: TypedEncoder[T]): RecordFieldEncoder[T] = new RecordFieldEncoder[T](e)
implicit def apply[T](implicit e: TypedEncoder[T]): RecordFieldEncoder[T] = new RecordFieldEncoder[T](e, e.jvmRepr, e.fromCatalyst, e.toCatalyst)
}
Loading