Skip to content

Commit

Permalink
Add TypeableRow, ManyTuple -> XLTuple, fix full handling
Browse files Browse the repository at this point in the history
Conflicts:
	build.sbt
  • Loading branch information
OlivierBlanvillain committed Jan 4, 2016
1 parent 834d1bd commit 04e733f
Show file tree
Hide file tree
Showing 9 changed files with 387 additions and 279 deletions.
18 changes: 9 additions & 9 deletions project/Boilerplate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ object Boilerplate {
val header = "// Auto-generated boilerplate"

val templates: Seq[Template] = List(
GenManyTuples,
GenManyTuplerInstances
GenXLTuples,
GenXLTuplerInstances
)

def gen(dir: File) =
Expand Down Expand Up @@ -56,8 +56,8 @@ object Boilerplate {
}
}

object GenManyTuples extends Template(23, 64) {
def filename(root: File) = root / "ManyTuples.scala"
object GenXLTuples extends Template(23, 64) {
def filename(root: File) = root / "XLTuples.scala"

def content(tv: TemplateVals) = {
import tv._
Expand Down Expand Up @@ -88,20 +88,20 @@ object Boilerplate {
}
}

object GenManyTuplerInstances extends Template(1, 64) {
def filename(root: File) = root / "ManyTuplerInstances.scala"
object GenXLTuplerInstances extends Template(1, 64) {
def filename(root: File) = root / "XLTuplerInstances.scala"
def content(tv: TemplateVals) = {
import tv._
block"""
|package typedframe
|
|import shapeless._
|
|trait ManyTuplerInstances {
| type Aux[L <: HList, Out0] = ManyTupler[L] { type Out = Out0 }
|trait XLTuplerInstances {
| type Aux[L <: HList, Out0] = XLTupler[L] { type Out = Out0 }
-
- implicit def hlistTupler${arity}[${`A..N`}]: Aux[${`A::N`}, ${`(A..N)`}] =
- new ManyTupler[${`A::N`}] {
- new XLTupler[${`A::N`}] {
- type Out = ${`(A..N)`}
- def apply(l : ${`A::N`}): Out = l match { case ${`a::n`} => ${`(a..n)`} }
- }
Expand Down
8 changes: 4 additions & 4 deletions src/main/scala/GroupedTypedFrame.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ final class GroupedTypedFrame[Schema <: Product, GroupingColumns <: HList](gd: G
n: Length.Aux[C, N],
f: Fill.Aux[N, Double, F],
p: Prepend.Aux[S, F, E],
t: ManyTupler.Aux[E, Out],
t: XLTupler.Aux[E, Out],
b: LabelledGeneric.Aux[Out, B],
y: Keys.Aux[B, Y],
o: ToList[Y, Symbol]
Expand All @@ -47,7 +47,7 @@ final class GroupedTypedFrame[Schema <: Product, GroupingColumns <: HList](gd: G
r: SelectAll.Aux[G, GroupingColumns, S],
m: Mapper.Aux[ToPreciseNumeric.type, U, O],
p: Prepend.Aux[S, O, E],
t: ManyTupler.Aux[E, Out],
t: XLTupler.Aux[E, Out],
b: LabelledGeneric.Aux[Out, B],
y: Keys.Aux[B, Y],
o: ToList[Y, Symbol]
Expand All @@ -69,7 +69,7 @@ final class GroupedTypedFrame[Schema <: Product, GroupingColumns <: HList](gd: G
r: SelectAll.Aux[G, GroupingColumns, S],
n: Length.Aux[C, N],
p: Prepend.Aux[S, U, E],
t: ManyTupler.Aux[E, Out],
t: XLTupler.Aux[E, Out],
b: LabelledGeneric.Aux[Out, B],
y: Keys.Aux[B, Y],
o: ToList[Y, Symbol]
Expand All @@ -85,7 +85,7 @@ final class GroupedTypedFrame[Schema <: Product, GroupingColumns <: HList](gd: G
g: LabelledGeneric.Aux[Schema, G],
s: SelectAll.Aux[G, GroupingColumns, S],
p: Prepend.Aux[S, Long :: HNil, P],
t: ManyTupler.Aux[P, Out],
t: XLTupler.Aux[P, Out],
b: LabelledGeneric.Aux[Out, B],
y: Keys.Aux[B, Y],
o: ToList[Y, Symbol]
Expand Down
105 changes: 99 additions & 6 deletions src/main/scala/ShapelessExtra.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package typedframe
import shapeless._
import shapeless.ops.hlist._
import shapeless.ops.record.Remover
import scala.collection.GenTraversable
import scala.language.higherKinds

/** Type class supporting multiple record field removal. */
Expand Down Expand Up @@ -31,9 +32,8 @@ object AllRemover {
}
}

// https://github.com/milessabin/shapeless/pull/503

/** Typeclass witnessing that all the elements of an HList have instances of the given typeclass. */
/** Typeclass witnessing that all the elements of an HList have instances of the given typeclass.
* (To be removed when https://github.com/milessabin/shapeless/pull/503 is published) */
sealed trait LiftAll[F[_], In <: HList] {
type Out <: HList
def instances: Out
Expand All @@ -57,8 +57,101 @@ object LiftAll {
}

/** Type class supporting conversion of this `HList` to a tuple, up to Tuple64. */
trait ManyTupler[L <: HList] extends DepFn1[L] with Serializable
trait XLTupler[L <: HList] extends DepFn1[L] with Serializable

object XLTupler extends XLTuplerInstances {
def apply[L <: HList](implicit tupler: XLTupler[L]): Aux[L, tupler.Out] = tupler
}

/** Type class supporting type safe cast.
* Differs from shapeless.Typeable for it's null support.*/
trait NullTypeable[T] extends Serializable {
def cast(t: Any): Option[T]
}

object NullTypeable {
// implicit val nullTypeableany: NullTypeable[Any] = nullTypeableFromTypeable(Typeable.anyTypeable)

implicit def nullTypeableFromTypeable[T](implicit typeable: Typeable[T]): NullTypeable[T] =
new NullTypeable[T] {
def cast(t: Any): Option[T] =
if(t == null) Some(null.asInstanceOf[T]) else typeable.cast(t)
}
}

/** Type class supporting type safe conversion of `Traversables` to `HLists`.
* Differs from shapeless.ops.traversable.FromTraversable for it's null support. */
trait FromTraversableNullable[Out <: HList] extends Serializable {
def apply(l: GenTraversable[_]): Option[Out]
}

object ManyTupler extends ManyTuplerInstances {
def apply[L <: HList](implicit tupler: ManyTupler[L]): Aux[L, tupler.Out] = tupler
object FromTraversableNullable {
def apply[Out <: HList](implicit from: FromTraversableNullable[Out]) = from

implicit def hnilFromTraversableNullable[T]: FromTraversableNullable[HNil] =
new FromTraversableNullable[HNil] {
def apply(l: GenTraversable[_]) =
if(l.isEmpty) Some(HNil) else None
}

implicit def hlistFromTraversableNullable[OutH, OutT <: HList]
(implicit
flt: FromTraversableNullable[OutT],
oc: NullTypeable[OutH]
): FromTraversableNullable[OutH :: OutT] =
new FromTraversableNullable[OutH :: OutT] {
def apply(l: GenTraversable[_]): Option[OutH :: OutT] =
if(l.isEmpty) None else for(h <- oc.cast(l.head); t <- flt(l.tail)) yield h :: t
}
}

import scala.language.experimental.macros
import scala.reflect.macros.whitebox

trait IsXLTuple[T]

object IsXLTuple {
implicit def apply[T]: IsXLTuple[T] = macro IsXLTupleMacro.mk[T]
}

class IsXLTupleMacro(val c: whitebox.Context) {
import c.universe._
import internal.constantType
import Flag._

def abort(msg: String) =
c.abort(c.enclosingPosition, msg)

def mk[T: WeakTypeTag]: Tree = {
val tTpe = weakTypeOf[T]
if(!isTuple(tTpe))
abort(s"Unable to materialize IsXLTuple for non-tuple type $tTpe")

q"""new IsXLTuple[$tTpe] {}"""
}

def isTuple(tpe: Type): Boolean =
tpe <:< typeOf[Unit] ||
tpe <:< typeOf[Tuple1[_]] ||
tpe <:< typeOf[(_, _)] ||
tpe <:< typeOf[(_, _, _)] ||
tpe <:< typeOf[(_, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _)] ||
tpe <:< typeOf[(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _)]
}
41 changes: 41 additions & 0 deletions src/main/scala/TypeableRow.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package typedframe

import shapeless._
import shapeless.ops.hlist.{Length, Fill}
import scala.reflect.runtime.universe.TypeTag
import org.apache.spark.sql.Row

trait TypeableRow[S <: Product] {
def apply(row: Row): S
}

trait LowPriorityTypeableRow {
implicit def typeableRowProduct[S <: Product, G <: HList]
(implicit
c: TypeTag[G],
g: Generic.Aux[S, G],
n: FromTraversableNullable[G]
): TypeableRow[S] =
new TypeableRow[S] {
def apply(row: Row): S = n(row.toSeq).fold(fail(row))(g.from)
}

protected def fail[G](row: Row)(implicit c: TypeTag[G]) =
throw new RuntimeException(s"Type error: failed to cast row $row of type ${row.schema} to $c")
}

object TypeableRow extends LowPriorityTypeableRow {
implicit def typeableRowTuple[S <: Product, G <: HList, N <: Nat, F <: HList, T <: Product]
(implicit
c: TypeTag[G],
t: IsXLTuple[S],
g: Generic.Aux[S, G],
l: Length.Aux[G, N],
f: Fill.Aux[N, Any, F],
n: FromTraversableNullable[F],
p: XLTupler.Aux[F, T]
): TypeableRow[S] =
new TypeableRow[S] {
def apply(row: Row): S = n(row.toSeq).fold(fail(row))(l => p(l).asInstanceOf[S])
}
}
Loading

0 comments on commit 04e733f

Please sign in to comment.