-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
320 additions
and
21 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 |
---|---|---|
@@ -1,4 +1,4 @@ | ||
name := "shapeless-utils" | ||
version := "1.0.1" | ||
version := "1.1.0-SNAPSHOT" | ||
deps += shapeless | ||
addScala212 |
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 |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package hammerlab | ||
|
||
import org.hammerlab.shapeless.all | ||
|
||
object shapeless extends all |
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 |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package org.hammerlab.shapeless | ||
|
||
import org.hammerlab.shapeless.coproduct.{ cast, singleton } | ||
import org.hammerlab.shapeless.hlist.{ HasFlattenedOps, HasSelectOps } | ||
import org.hammerlab.shapeless.record.HasFieldOps | ||
import shapeless.HNil | ||
|
||
trait all | ||
extends record.HasFindOps | ||
with HasFieldOps | ||
with hlist.HasFindOps | ||
with HasFlattenedOps | ||
with HasSelectOps | ||
with cast | ||
with singleton { | ||
val ⊥ = HNil | ||
type ⊥ = HNil | ||
} |
20 changes: 20 additions & 0 deletions
20
src/main/scala/org/hammerlab/shapeless/coproduct/Iso.scala
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 |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package org.hammerlab.shapeless.coproduct | ||
|
||
import shapeless.Generic | ||
|
||
abstract class Iso[From, To](f: From, val t: To) { | ||
def from(to: To): From | ||
def apply(fn: To ⇒ To): From = | ||
from( | ||
fn( | ||
t | ||
) | ||
) | ||
} | ||
|
||
object Iso { | ||
implicit def apply[T, L](t: T)(implicit gen: Generic.Aux[T, L]): Iso[T, L] = | ||
new Iso[T, L](t, gen.to(t)) { | ||
override def from(to: L) = gen.from(to) | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
src/main/scala/org/hammerlab/shapeless/coproduct/cast.scala
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 |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package org.hammerlab.shapeless.coproduct; | ||
|
||
import shapeless._ | ||
|
||
trait cast { | ||
/** | ||
* Type-class providing evidence that type [[T]] is structurally equivalent to an [[HList]]-type `L` | ||
* | ||
* Additionally provides a round-trip into and out of `L` for a given instance of [[T]] | ||
*/ | ||
trait Cast[T] { | ||
type L <: HList | ||
def iso(t: T): Iso[T, L] | ||
def apply(t: T)(fn: L ⇒ L): T = iso(t)(fn) | ||
} | ||
|
||
object Cast { | ||
|
||
type Aux[T, L0 <: HList] = Cast[T] { type L = L0 } | ||
|
||
implicit def cnil[L0 <: HList]: Aux[CNil, L0] = | ||
new Cast[CNil] { | ||
type L = L0 | ||
override def iso(t: CNil) = ??? | ||
} | ||
|
||
implicit def coproduct[H, T <: Coproduct, L0 <: HList](implicit | ||
genH: Generic.Aux[H, L0], | ||
lt: Aux[T, L0]): Aux[H :+: T, L0] = | ||
new Cast[H :+: T] { | ||
type L = L0 | ||
override def iso(t: H :+: T) = | ||
t match { | ||
case Inl(h) ⇒ | ||
new Iso[H :+: T, L]( | ||
Inl(h), | ||
genH.to(h) | ||
) { | ||
override def from(to: L) = Inl(genH.from(to)) | ||
} | ||
case Inr(t) ⇒ | ||
val iso = lt.iso(t) | ||
new Iso[H :+: T, L]( | ||
Inr(t), | ||
iso.t | ||
) { | ||
override def from(to: L) = Inr(iso.from(to)) | ||
} | ||
} | ||
} | ||
|
||
implicit def sealedTrait[T, C <: Coproduct, L0 <: HList](implicit | ||
genT: Generic.Aux[T, C], | ||
l: Aux[C, L0]): Aux[T, L0] = | ||
new Cast[T] { | ||
type L = L0 | ||
override def iso(t: T) = { | ||
val c = genT.to(t) | ||
val iso = l.iso(c) | ||
new Iso[T, L](t, iso.t) { | ||
override def from(to: L) = genT.from(iso.from(to)) | ||
} | ||
} | ||
} | ||
|
||
implicit def caseClass[T, L0 <: HList](implicit genT: Generic.Aux[T, L0]): Aux[T, L0] = | ||
new Cast[T] { | ||
type L = L0 | ||
override def iso(t: T) = Iso[T, L0](t) | ||
} | ||
|
||
} | ||
|
||
implicit class CastOps[T, L <: HList](t: T)(implicit c: Cast.Aux[T, L]) { | ||
def map(fn: L ⇒ L): T = c(t)(fn) | ||
} | ||
} | ||
|
||
object cast extends cast with Serializable |
30 changes: 30 additions & 0 deletions
30
src/main/scala/org/hammerlab/shapeless/coproduct/singleton.scala
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 |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package org.hammerlab.shapeless.coproduct | ||
|
||
import cast._ | ||
import shapeless._ | ||
|
||
trait singleton { | ||
/** | ||
* Type-class representing evidence that a type [[T]] wraps a single value of type [[V]]. | ||
* | ||
* [[T]] may be a sealed hierarchy as long as all concrete implementations are case-classes with a single [[V]] | ||
* parameter. | ||
*/ | ||
trait Singleton[T, V] { | ||
def apply(t: T)(fn: V ⇒ V): T | ||
} | ||
|
||
object Singleton { | ||
def apply[T, V](implicit l: Cast.Aux[T, V :: HNil]): Singleton[T, V] = fromLooksLike[T, V] | ||
|
||
implicit def fromLooksLike[T, V](implicit l: Cast.Aux[T, V :: HNil]): Singleton[T, V] = | ||
new Singleton[T, V] { | ||
override def apply(t: T)(fn: V ⇒ V) = | ||
l(t)(l ⇒ fn(l.head) :: HNil) | ||
} | ||
} | ||
|
||
implicit class SingletonOps[T, V](t: T)(implicit e: Singleton[T, V]) { | ||
def map(fn: V ⇒ V) = e(t)(fn) | ||
} | ||
} |
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 |
---|---|---|
@@ -1,15 +1,3 @@ | ||
package org.hammerlab | ||
|
||
import _root_.shapeless.HNil | ||
import org.hammerlab.shapeless.hlist.{ HasFlattenedOps, HasSelectOps } | ||
import org.hammerlab.shapeless.record.HasFieldOps | ||
|
||
package object shapeless | ||
extends record.HasFindOps | ||
with HasFieldOps | ||
with hlist.HasFindOps | ||
with HasFlattenedOps | ||
with HasSelectOps { | ||
val ⊥ = HNil | ||
type ⊥ = HNil | ||
} | ||
package object shapeless extends all |
43 changes: 43 additions & 0 deletions
43
src/test/scala/org/hammerlab/shapeless/coproduct/CastTest.scala
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 |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package org.hammerlab.shapeless.coproduct | ||
|
||
import org.hammerlab.test.Suite | ||
|
||
object CastTest { | ||
sealed trait X | ||
case class Y(n: Int, s: String) extends X | ||
case class Z(n: Int, s: String) extends X | ||
} | ||
|
||
class CastTest | ||
extends Suite { | ||
|
||
import CastTest._ | ||
import hammerlab.shapeless._ | ||
import shapeless._ | ||
|
||
test("apply") { | ||
val y = Y(111, "abc") | ||
val z = Z(222, "def") | ||
|
||
val xs = Seq[X](y, z) | ||
|
||
y map { | ||
case n :: s :: ⊥ ⇒ | ||
2*n :: s.reverse :: ⊥ | ||
} should be( | ||
Y(222, "cba") | ||
) | ||
|
||
xs map { | ||
_ map { | ||
case n :: s :: ⊥ ⇒ | ||
10*n :: s*2 :: ⊥ | ||
} | ||
} should be( | ||
Seq( | ||
Y(1110, "abcabc"), | ||
Z(2220, "defdef") | ||
) | ||
) | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
src/test/scala/org/hammerlab/shapeless/coproduct/SingletonTest.scala
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 |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package org.hammerlab.shapeless.coproduct | ||
|
||
import hammerlab.shapeless._ | ||
import org.hammerlab.test.Suite | ||
|
||
object SingletonTest { | ||
|
||
/** | ||
* Sample sealed hierarchy where all implementations are [[Int]] [[Singleton]]s | ||
*/ | ||
sealed trait Foo | ||
|
||
case class A(n: Int) extends Foo | ||
|
||
sealed trait Bar extends Foo | ||
case class B(n: Int) extends Bar | ||
case class C(n: Int) extends Bar | ||
case class D(n: Int) extends Bar | ||
|
||
/** | ||
* Hang a multiplication ([[*]]) operator off any [[Int]]-[[Singleton]], in this case [[Bar]] and its subclasses. | ||
*/ | ||
implicit class Ops[U](val t: U) extends AnyVal { | ||
def *(n: Int)(implicit e: Singleton[U, Int]): U = t map(_ * n) | ||
} | ||
} | ||
|
||
class SingletonTest | ||
extends Suite { | ||
|
||
import SingletonTest._ | ||
|
||
test("coproduct") { | ||
// mapping preserves type | ||
val a: A = A(2) * 10 | ||
|
||
A(2) * 10 should be(A(20)) | ||
B(2) * 10 should be(B(20)) | ||
C(2) * 10 should be(C(20)) | ||
D(2) * 10 should be(D(20)) | ||
|
||
val foo: Foo = A(2) | ||
foo * (10) should be(A(20)) | ||
|
||
val bar: Bar = B(2) | ||
bar * (10) should be(B(20)) | ||
|
||
// test summoning | ||
val _: Singleton[Foo, Int] = Singleton[Foo, Int] | ||
} | ||
} |