-
Notifications
You must be signed in to change notification settings - Fork 531
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
4d135ec
commit d4c3c71
Showing
4 changed files
with
103 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -268,6 +268,7 @@ trait ReprTypes { | |
|
||
def atatTpe = typeOf[tag.@@[_,_]].typeConstructor | ||
def fieldTypeTpe = typeOf[shapeless.labelled.FieldType[_, _]].typeConstructor | ||
def keyTagTpe = typeOf[shapeless.labelled.KeyTag[_, _]].typeConstructor | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
milessabin
Author
Owner
|
||
} | ||
|
||
trait CaseClassMacros extends ReprTypes { | ||
|
@@ -447,6 +448,31 @@ trait CaseClassMacros extends ReprTypes { | |
def mkCoproductTpe(items: List[Type]): Type = | ||
mkCompoundTpe(cnilTpe, cconsTpe, items) | ||
|
||
def unpackHListTpe(tpe: Type): List[Type] = { | ||
@tailrec | ||
def unfold(u: Type, acc: List[Type]): List[Type] = { | ||
val HNilTpe = hnilTpe | ||
val HConsPre = prefix(hconsTpe) | ||
val HConsSym = hconsTpe.typeSymbol | ||
u.dealias match { | ||
This comment has been minimized.
Sorry, something went wrong.
retronym
Contributor
|
||
case t if t <:< HNilTpe => acc | ||
case TypeRef(pre, HConsSym, List(hd, tl)) if pre =:= HConsPre => unfold(tl, hd :: acc) | ||
case _ => abort(s"$tpe is not an HList type") | ||
} | ||
} | ||
|
||
unfold(tpe, List()).reverse | ||
} | ||
|
||
def unpackFieldType(tpe: Type): (Type, Type) = { | ||
val KeyTagPre = prefix(keyTagTpe) | ||
val KeyTagSym = keyTagTpe.typeSymbol | ||
tpe.dealias match { | ||
case RefinedType(List(v0, TypeRef(pre, KeyTagSym, List(k, v1))), _) if pre =:= KeyTagPre && v0 =:= v1 => (k, v0) | ||
case _ => abort(s"$tpe is not a field type") | ||
} | ||
} | ||
|
||
def mkTypTree(tpe: Type): Tree = { | ||
tpe match { | ||
case SingleType(pre @ SingleType(_, _), sym) => | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,9 @@ | |
package shapeless | ||
package ops | ||
|
||
import scala.language.experimental.macros | ||
import scala.reflect.macros.{ blackbox, whitebox } | ||
|
||
import poly._ | ||
|
||
//object record { | ||
|
@@ -37,25 +40,39 @@ package record { | |
def apply(l : L): Out | ||
} | ||
|
||
trait LowPrioritySelector { | ||
object Selector { | ||
type Aux[L <: HList, K, Out0] = Selector[L, K] { type Out = Out0 } | ||
|
||
implicit def hlistSelect[H, T <: HList, K] | ||
(implicit st : Selector[T, K]): Aux[H :: T, K, st.Out] = | ||
new Selector[H :: T, K] { | ||
type Out = st.Out | ||
def apply(l : H :: T): Out = st(l.tail) | ||
} | ||
def apply[L <: HList, K](implicit selector: Selector[L, K]): Aux[L, K, selector.Out] = selector | ||
|
||
implicit def mkSelector[L <: HList, K, O]: Aux[L, K, O] = macro SelectorMacros.applyImpl[L, K] | ||
} | ||
|
||
object Selector extends LowPrioritySelector { | ||
def apply[L <: HList, K](implicit selector: Selector[L, K]): Aux[L, K, selector.Out] = selector | ||
class SelectorMacros(val c: whitebox.Context) extends CaseClassMacros { | ||
import c.universe._ | ||
|
||
def applyImpl[L <: HList, K](implicit lTag: WeakTypeTag[L], kTag: WeakTypeTag[K]): Tree = { | ||
val lTpe = lTag.tpe.dealias | ||
val kTpe = kTag.tpe.dealias | ||
if(!(lTpe <:< hlistTpe)) | ||
abort(s"$lTpe is not a record type") | ||
|
||
implicit def hlistSelect1[K, V, T <: HList]: Aux[FieldType[K, V] :: T, K, V] = | ||
new Selector[FieldType[K, V] :: T, K] { | ||
type Out = V | ||
def apply(l : FieldType[K, V] :: T): Out = l.head | ||
val lTpes = unpackHListTpe(lTpe).zipWithIndex.flatMap { case (fTpe, i) => | ||
val (k, v) = unpackFieldType(fTpe) | ||
if(k.dealias =:= kTpe) Some((v, i)) else None | ||
} | ||
lTpes.headOption match { | ||
case Some((vTpe, i)) => | ||
q""" | ||
new _root_.shapeless.ops.record.Selector[$lTpe, $kTpe] { | ||
type Out = $vTpe | ||
def apply(l: $lTpe): $vTpe = _root_.shapeless.HList.unsafeGet(l, $i).asInstanceOf[$vTpe] | ||
}: _root_.shapeless.ops.record.Selector.Aux[$lTpe, $kTpe, $vTpe] | ||
""" | ||
case _ => | ||
abort(s"No field $kTpe in record type $lTpe") | ||
} | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -95,31 +112,38 @@ package record { | |
*/ | ||
trait Updater[L <: HList, F] extends DepFn2[L, F] with Serializable { type Out <: HList } | ||
|
||
trait LowPriorityUpdater { | ||
object Updater { | ||
type Aux[L <: HList, F, Out0 <: HList] = Updater[L, F] { type Out = Out0 } | ||
|
||
implicit def hlistUpdater1[H, T <: HList, K, V] | ||
(implicit ut : Updater[T, FieldType[K, V]]): Aux[H :: T, FieldType[K, V], H :: ut.Out] = | ||
new Updater[H :: T, FieldType[K, V]] { | ||
type Out = H :: ut.Out | ||
def apply(l: H :: T, f: FieldType[K, V]): Out = l.head :: ut(l.tail, f) | ||
} | ||
def apply[L <: HList, F](implicit updater: Updater[L, F]): Aux[L, F, updater.Out] = updater | ||
|
||
implicit def mkUpdater[L <: HList, F, O]: Aux[L, F, O] = macro UpdaterMacros.applyImpl[L, F] | ||
} | ||
|
||
object Updater extends LowPriorityUpdater { | ||
def apply[L <: HList, F](implicit updater: Updater[L, F]): Aux[L, F, updater.Out] = updater | ||
class UpdaterMacros(val c: whitebox.Context) extends CaseClassMacros { | ||
import c.universe._ | ||
|
||
implicit def hnilUpdater[L <: HNil, F]: Aux[L, F, F :: HNil] = | ||
new Updater[L, F] { | ||
type Out = F :: HNil | ||
def apply(l: L, f: F): Out = f :: HNil | ||
} | ||
def applyImpl[L <: HList, F](implicit lTag: WeakTypeTag[L], fTag: WeakTypeTag[F]): Tree = { | ||
val lTpe = lTag.tpe.dealias | ||
val fTpe = fTag.tpe.dealias | ||
if(!(lTpe <:< hlistTpe)) | ||
abort(s"$lTpe is not a record type") | ||
|
||
implicit def hlistUpdater2[K, V, T <: HList]: Aux[FieldType[K, V] :: T, FieldType[K, V], FieldType[K, V] :: T] = | ||
new Updater[FieldType[K, V] :: T, FieldType[K, V]] { | ||
type Out = FieldType[K, V] :: T | ||
def apply(l: FieldType[K, V] :: T, f: FieldType[K, V]): Out = f :: l.tail | ||
val lTpes = unpackHListTpe(lTpe) | ||
val (uTpes, i) = { | ||
val i0 = lTpes.indexWhere(_.dealias =:= fTpe) | ||
This comment has been minimized.
Sorry, something went wrong.
retronym
Contributor
|
||
if(i0 < 0) (lTpes :+ fTpe, lTpes.length) | ||
else (lTpes.updated(i0, fTpe), i0) | ||
} | ||
val uTpe = mkHListTpe(uTpes) | ||
q""" | ||
new _root_.shapeless.ops.record.Updater[$lTpe, $fTpe] { | ||
type Out = $uTpe | ||
def apply(l: $lTpe, f: $fTpe): $uTpe = | ||
_root_.shapeless.HList.unsafeUpdate(l, $i, f).asInstanceOf[$uTpe] | ||
}: _root_.shapeless.ops.record.Updater.Aux[$lTpe, $fTpe, $uTpe] | ||
""" | ||
} | ||
} | ||
|
||
/** | ||
|
I'd be inclined to make all these defintions into
lazy vals
so you only pay the cost to look them up once per macro expansion. (It would be better to make it once-per-compiler run, but the macro engine doesn't make that sort of caching super easy...)