In object Sized (in "shapeless/sized.scala") there is unapplySeq, which unfortunately doesn't provide static checking. For example code below will fail at runtime with MatchError:
Sized(1, 2, 3) match { case Sized(x, y) => ":(" }
It would be better if there was unapply method instead, returning Option of tuple, and concrete shape of tuple was constructed according to size of Sized instance. For example:
Sized(1) => x
Sized(1, 2) => (x, y)
Sized(1, 2, 3) => (x, y, z)
In that case previous code snippet would fail to compile with constructor cannot be instantiated to expected type.
Please help me implement unapply for object Sized. Is this method already implemented anywhere?
Thanks in advance!
This is definitely possible (at least for Sized where N is less than 23), but the only approach I can think of (barring macros, etc.) is kind of messy. First we need a type class that'll help us convert sized collections to HLists:
import shapeless._, Nat._0
import scala.collection.generic.IsTraversableLike
trait SizedToHList[R, N <: Nat] extends DepFn1[Sized[R, N]] {
type Out <: HList
}
object SizedToHList {
type Aux[R, N <: Nat, Out0 <: HList] = SizedToHList[R, N] { type Out = Out0 }
implicit def emptySized[R]: Aux[R, Nat._0, HNil] = new SizedToHList[R, _0] {
type Out = HNil
def apply(s: Sized[R, _0]) = HNil
}
implicit def otherSized[R, M <: Nat, T <: HList](implicit
sth: Aux[R, M, T],
itl: IsTraversableLike[R]
): Aux[R, Succ[M], itl.A :: T] = new SizedToHList[R, Succ[M]] {
type Out = itl.A :: T
def apply(s: Sized[R, Succ[M]]) = s.head :: sth(s.tail)
}
def apply[R, N <: Nat](implicit sth: SizedToHList[R, N]): Aux[R, N, sth.Out] =
sth
def toHList[R, N <: Nat](s: Sized[R, N])(implicit
sth: SizedToHList[R, N]
): sth.Out = sth(s)
}
And then we can define an extractor object that uses this conversion:
import ops.hlist.Tupler
object SafeSized {
def unapply[R, N <: Nat, L <: HList, T <: Product](s: Sized[R, N])(implicit
itl: IsTraversableLike[R],
sth: SizedToHList.Aux[R, N, L],
tupler: Tupler.Aux[L, T]
): Option[T] = Some(sth(s).tupled)
}
And then:
scala> val SafeSized(x, y, z) = Sized(1, 2, 3)
x: Int = 1
y: Int = 2
z: Int = 3
But:
scala> val SafeSized(x, y) = Sized(1, 2, 3)
<console>:18: error: wrong number of arguments for object SafeSized
val SafeSized(x, y) = Sized(1, 2, 3)
^
<console>:18: error: recursive value x$1 needs type
val SafeSized(x, y) = Sized(1, 2, 3)
^
As desired.
Related
I have a Slick (grouping) query returning a tuple of integers which should be then combined using one of SQL aggregation functions. The size of the tuple is not dynamic but I would like to be able to define it in a single place with possibility to change it later. I use shapeless elsewhere in my project so it is natural to use it here as well.
So, let's say we have a tuple type type Agg = (Rep[Int], Rep[Int], Rep[Int]) and a query with type Query[Agg, _, Seq]
I can then define poly's for aggregation like this:
object sum extends (Query[Rep[Int], Int, Seq] -> Rep[Int])(_.sum.getOrElse(0))
object sumAgg extends(Query[Agg, _, Seq] -> Agg)(q => (q.map(_._1), q.map(_._2), q.map(_._3)).map(sum))
But I can't find a way to get rid of explicit tuple unzipping in sumAgg poly. How can I transform a Query of tuple of Int's (aka Agg) into a tuple of queries of Int's?
Let's even simplify this. Supposing I have
val hlist = 1 :: 2 :: 3 :: HNil
val tuple = (4, 5, 6)
hlist.zipWithIndex.map(m)
What would be the definition of m to produce, say, a hlist of (1*4) :: (2*5) :: (3*6) :: HNil? I know I could directly zip hlist with tuple but in this scenario I think I do need to pick tuple elements one by one by their positions.
Try to replace sumAgg with type classes.
import shapeless.{::, HList, HNil, Nat, Poly1, Succ}
import shapeless.nat._
import shapeless.poly.->
import shapeless.syntax.std.tuple._
import shapeless.ops.hlist.Tupler
import shapeless.ops.tuple.{At, Length, Mapper}
import slick.lifted.{Query, Rep, Shape}
import slick.jdbc.PostgresProfile.api._
trait MkHList[Agg <: Product, N <: Nat] {
type Out <: HList
def apply(q: Query[Agg, _, Seq]): Out
}
object MkHList {
type Aux[Agg <: Product, N <: Nat, Out0 <: HList] = MkHList[Agg, N] { type Out = Out0 }
def instance[Agg <: Product, N <: Nat, Out0 <: HList](f: Query[Agg, _, Seq] => Out0): Aux[Agg, N, Out0] = new MkHList[Agg, N] {
override type Out = Out0
override def apply(q: Query[Agg, _, Seq]): Out = f(q)
}
implicit def zero[Agg <: Product]: Aux[Agg, _0, HNil] = instance(_ => HNil)
implicit def succ[Agg <: Product, N <: Nat, A](implicit
tailMkHList: MkHList[Agg, N],
at: At.Aux[Agg, N, Rep[A]],
shape: Shape[_ <: FlatShapeLevel, Rep[A], A, Rep[A]]
): Aux[Agg, Succ[N], Query[Rep[A], A, Seq] :: tailMkHList.Out] =
instance(q => q.map(_.at[N]) :: tailMkHList(q))
}
trait SumAgg[Agg <: Product] {
def apply(q: Query[Agg, _, Seq]): Agg
}
object SumAgg {
implicit def mkSumAgg[Agg <: Product, N <: Nat, L <: HList, Tpl <: Product](implicit
length: Length.Aux[Agg, N],
mkHList: MkHList.Aux[Agg, N, L],
tupler: Tupler.Aux[L, Tpl],
mapper: Mapper.Aux[Tpl, sum.type, Agg]
): SumAgg[Agg] = q => tupler(mkHList(q)).map(sum)
}
def sumAgg[Agg <: Product](q: Query[Agg, _, Seq])(implicit sumAggInst: SumAgg[Agg]): Agg = sumAggInst(q)
type Agg = (Rep[Int], Rep[Int], Rep[Int])
sumAgg(??? : Query[Agg, _, Seq]): Agg
Answering your simplified version:
import shapeless._
import syntax.std.tuple._ // brings implicits for tuple.productElements
// defines your polymorphic mapper
object MulPoly extends Poly1 {
// you're going to need one case for each pair of types that you might face.
// E.g. (Int, Int), (Int, String), (String, String), etc.
implicit val intPairCase: Case.Aux[(Int, Int), Int] = at({ case (a, b) => a * b })
}
val hlist = 1 :: 2 :: 3 :: HNil
val tuple = (4, 5, 6)
val tupleHList = tuple.productElements
>>> tupleHList: Int :: Int :: Int :: shapeless.HNil = 4 :: 5 :: 6 :: HNil
hlist
.zip(tupleHList) // intermediate result here: (1, 4) :: (2, 5) :: (3, 6) :: HNil
.map(MulPoly) // map MulPoly over the HList
>>> res0: Int :: Int :: Int :: shapeless.HNil = 4 :: 10 :: 18 :: HNil
So, essentially, your m is a Poly1, that can map on 2-tuples of types that constitute your hlist and a tuple.
You might want to consult with Type Astronaut's Guide to Shapeless, Section 7 about ploymorphic mapping, and this stackoverflow question about how to get an hlist out of any tuple.
I am writing an internal DSL, and am using Shapeless to enforce type safety. However, I have got stuck with an issue.
The simplified version of the problem is as follows.
Consider the code snippet below:
import shapeless._
import syntax.std.function._
import ops.function._
implicit class Ops[P <: Product, L <: HList](p: P)(implicit val gen: Generic.Aux[P, L]) {
def ~|>[F, R](f: F)(implicit fp: FnToProduct.Aux[F, L ⇒ R]) =
f.toProduct(gen.to(p))
}
(1, 2) ~|> ((x: Int, y: Int) ⇒ x + y) // -> at compile time, it ensures the types are aligned.
(1, 2, 3) ~|> ((x: Int, y: Int, z: Int) ⇒ x + y + z) // -> compiles okay
(1, "2", 3) ~|> ((x: Int, y: Int, z: Int) ⇒ x + y + z) // -> gives a compile error, as expected.
However, instead of A, I would like to use a container type Place[A].
case class Place[A](a: A)
val a = Place[Int](1)
val b = Place[Int]("str")
and also ensures the types are aligned with respect to the type parameters.
(a, b) ~|> ((x: Int, y: String) ⇒ x.toString + y)
That is, in the above case, I would like the types to be checked based on the type parameter of Place[_], which in the above case, Int and String respectively.
I highly appreciate your help!
You can do that with a combination of Unwrapped and LiftAll.
Unwrapped lets you extract the content of an AnyVal, LiftAll summons a given type class for every entry of an HList. If I understand correctly what you are trying to do, it could look like the following:
case class Place[A](a: A) extends AnyVal // cuz why not?
implicit class Ops[P <: Product, L <: HList, U <: HList, C <: HList](p: P)
(implicit
val g: Generic.Aux[P, L],
val l: LiftAll.Aux[Unwrapped, L, U],
val c: Comapped.Aux[Place, L, C]
) {
def ~|>[F, R](f: F)(implicit fp: FnToProduct.Aux[F, C ⇒ R]) = {
def fuckitZip(l1: HList, l2: HList): HList = (l1, l2) match {
case (h1 :: t1, h2 :: t2) => (h1, h2) :: fuckitZip(l1, l2)
case _ => HNil
}
def fuckitMap(l: HList, f: Any => Any): HList = l match {
case HNil => HNil
case h :: t => f(h) :: fuckitMap(t, f)
}
def f(a: Any): Any = a match {
case (x: Any, y: Any) =>
x.asInstanceOf[Unwrapped[Any] { type U = Any }].unwrap(y)
}
val zp = fuckitZip(g.to(p), l.instances)
val uw = fuckitMap(zp, f _).asInstanceOf[C]
f.toProduct(uw)
}
}
Note that here I've used Comapped and an unsafe zip/map to keep it simple, making it typesafe with a proper HList zip/map is left as an exercise.
As usually with these complex transformations it might be simpler (and will run/compiler faster) to reimplement everything with a dedicated type class by inlining everything, I just wanted to show that this is doable with primitive operations :)
I'm trying to find a way to avoid losing type information in the return value for my method.
I have the following:
val defs0 = Default.mkDefault[Person, Some[String] :: Some[Int] :: HNil](Some("odd") :: Some(42) :: HNil)
Using IntelliJs "Add type annotation" gives the type:
Default.Aux[Person, ::[Some[String], ::[Some[Int], HNil]]]
This is fine, except I dont want to specify the fields of Person when I call mkDefault. So I created this:
object MkDefault {
object toSome extends Poly1 {
implicit def default[P] = at[P](Some(_))
}
def apply[P, L <: HList, D <: HList]
(p: P)
(implicit
lg: LabelledGeneric.Aux[P, L],
mpr: Mapper.Aux[toSome.type, L, D]
): Default.Aux[P, D] =
Default.mkDefault[P, D](mpr(lg.to(p)))
}
Now I can do:
val defs1 = MkDefault(Person("odd", 42))
Which is good, except the inferred type from IntellJ looks like this:
Default.Aux[Person, HNil]
How can I make the inferred type of defs1 equal to the inferred type of defs0?
*without having to specify the fields of class Person
Use mpr.Out instead of D as an output type:
object MkDefault {
object toSome extends Poly1 {
implicit def default[P] = at[P](Some(_))
}
def apply[P, L <: HList, D <: HList]
(p: P)
(implicit
lg: LabelledGeneric.Aux[P, L],
mpr: Mapper.Aux[toSome.type, L, D]
): Default.Aux[P, mpr.Out] =
Default.mkDefault[P, D](mpr(lg.to(p)))
}
Example:
scala> val defs1 = MkDefault(Person("odd", 42))
defs1: shapeless.Default[Person]{type Out = shapeless.::[Some[String with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("name")],String]],shapeless.::[Some[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("age")],Int]],shapeless.HNil]]} = shapeless.Default$$anon$1#1f6a8bb8
P.S.
If you don't mind Option instead of Some, you can also use AsOptions
val opt = Default.AsOptions[Person]
val def3 = Default.mkDefault[Person, opt.Out](Some("aaa") :: Some(5) :: HNil)
Check:
scala> implicitly[def3.Out =:= ::[Option[String], ::[Option[Int], HNil]]]
res12: =:=[def3.Out,shapeless.::[Option[String],shapeless.::[Option[Int],shapeless.HNil]]] = <function1>
I'm working on an API that should enable to build a shapeless Poly1 function dynamically from standard monomorphic functions that operate on types of some coproduct.
The goal is to expose a simple method that receives a function as:
type FooCoproduct = Foo :+: Bar :+: CNil
def addF[E](f: E => E)(implicit ev: Inject[FooCoproduct, E]) = ???
and accumulate these functions in order to build a total Poly1 function covering all types in the coproduct. The evidence ev here is to force that the type paremeter E is a type in the coproduct.
After testing several approaches, including generic derivation of typeclasses, the most promising one has led me to accumulate these monomorphic functions in an HList and try to resolve the one that applies by means of a Selector. This is probably better understood by example:
object CoproductSample extends App {
import shapeless.{ :+:, CNil, Coproduct, HList, HNil, Poly1 }
import shapeless.ops.coproduct.Inject
import shapeless.ops.hlist.Selector
class Builder[A <: Coproduct] {
def accum[B](f: B => B, hl: HList)(implicit ev: Inject[A, B]) = f :: hl
class PolyBuilder[L <: HList](hl: L) extends Poly1 {
implicit def run[T](implicit ev: Selector[L, T => T]) =
at[T](hl.select[T => T])
}
}
type Cop = Int :+: String :+: CNil
val builder = new Builder[Cop]
val hl1 = builder.accum((i: Int) => i + 1, HNil)
val hl2 = builder.accum((s: String) => s + "one", hl1)
object pf extends builder.PolyBuilder(hl2)
val rInt = Coproduct[Cop](10).fold(pf)
val rStr = Coproduct[Cop]("ten").fold(pf)
}
This code doesn't compile with the message:
could not find implicit value for parameter folder:
shapeless.ops.coproduct.Folder[CoproductSample.pf.type, CoproductSample.Cop]
I suppose that I need to provide a Selector[L, T => T] where L is the type of the accumulated HList but I can't come up with the way to do this. On the other hand, I have the feeling that there must be a simpler solution to my problem.
Any help would be appreciated.
Update
After doing some more research I've come up with a solution that almost works. Unfortunately I'm not able to track the result type properly.
object CoproductSample {
import shapeless.{ CNil, Coproduct, HList, HNil, Inl, Inr, Poly2, ::, :+: }
// Accumulates ordinary functions from A => A in an HList
def accum[A, L <: HList](f: A => A, hl: L): (A => A) :: L = f :: hl
// A poly2 function that evaluates some monomorphic function present in
// an HList for certain value that satifies the signature of this function
object PolyEval extends Poly2 {
implicit def hnilCase[A]: Case.Aux[A, HNil, Option[A]] =
at[A, HNil]((a, l) => None)
implicit def hheadCaseSuccess[A, T <: HList]: Case.Aux[A, (A => A) :: T, Option[A]] =
at[A, (A => A) :: T]((a: A, l: (A => A) :: T) => Option(l.head(a)))
implicit def hheadCaseFail[A, H, T <: HList](
implicit tail: Case.Aux[A, T, Option[A]]
): Case.Aux[A, (H => H) :: T, Option[A]] =
at[A, (H => H) :: T]((a: A, l: (H => H) :: T) => PolyEval(a, l.tail))
}
// A poly2 function that uses `PolyEval` for evaluating a value present in
// a coproduct against an HList of monomorphic functions
object PolyEvalCop extends Poly2 {
implicit def cnilCase[A <: CNil, L <: HList]: Case.Aux[A, L, Option[A]] =
at[A, L]((a, l) => sys.error("Impossible!"))
implicit def cconsCase[H, T <: Coproduct, L <: HList](
implicit head: PolyEval.Case.Aux[H, L, Option[H]],
tail: Case[T, L]) // What is the return type here???)
= at[H :+: T, L]((c, l) =>
c match {
case Inl(h) => PolyEval(h, l)
case Inr(t) => PolyEvalCop(t, l)
})
}
}
Console session:
scala> import shapeless._, CoproductSample._
import shapeless._
import CoproductSample._
scala> case class Foo(i: Int); case class Bar(s: String)
defined class Foo
defined class Bar
scala> val f = (foo: Foo) => foo.copy(i = foo.i * 2)
f: Foo => Foo = <function1>
scala> val g = (bar: Bar) => bar.copy(s = bar.s + "_changed!")
g: Bar => Bar = <function1>
scala> val hl = accum(g, accum(f, HNil))
hl: shapeless.::[Bar => Bar,shapeless.::[Foo => Foo,shapeless.HNil.type]] = <function1> :: <function1> :: HNil
scala> type C = Foo :+: Bar :+: CNil
defined type alias C
scala> PolyEvalCop(Coproduct[C](Foo(10)), hl)
res1: Any = Some(Foo(20))
scala> PolyEvalCop(Coproduct[C](Bar("bar")), hl)
res2: Any = Some(Bar(bar_changed!))
The result type is not properly tracked and it's resolved as Any.
From the signature of addF it looks like you want to map over the Coproduct, i.e. modify it's value and stay on a coproduct, i.e. def add[E](e:E):E, if that were the case this would work:
# {
trait Add[E]{
def add(e:E):E
}
object Add{
def apply[E:Add]:Add[E] = implicitly[Add[E]]
implicit object cnil extends Add[CNil] {
def add(e:CNil) = throw new RuntimeException("Impossible")
}
implicit def coproduct[H, T <: Coproduct](
implicit
addH: Add[H],
addT:Add[T],
basis: ops.coproduct.Basis[H :+: T,T]
):Add[H :+: T] = new Add[H :+: T]{
def add(e: H :+: T) = e match {
case Inl(h) => Coproduct[H :+: T](addH.add(h)) // to stay in the Coproduct
case Inr(t) => addT.add(t).embed[H :+: T] // to stay in the coproduct
}
}
}
}
defined trait Add
defined object Add
# implicit def addString = new Add[String] {
def add(e:String) = e + "-ah"
}
defined function addString
# implicit def addInt = new Add[Int] {
def add(e:Int) = e + 1
}
defined function addInt
# type C = Int :+: String :+: CNil
defined type C
# val i = Coproduct[C](1)
i: C = 1
# Add[C].add(i)
res24: C = 2 // notice that the return type is C
# val s = Coproduct[C]("a")
s: C = a
# Add[C].add(s)
res26: C = a-ah // notice that the return type is C
It obviously works with "plain" types:
# Add[Int].add(1)
res38: Int = 2
The above is equivalent to map; but if you want a fold, i.e. def add[E](e:E):Int, you would just modify these two lines:
case Inl(h) => addH.add(h)
case Inr(t) => addT.add(t)
class P(name: String)
class E(_name: String, role: String) extends P(_name)
def testF[T <: P](x: List[T]): List[T] = x
val le = List(new E("Henry", "Boss"))
class Test[R <: E](l: List[R]) {
def r[O <: P] (): List[O] = testF(l)
}
I get:
Error:(8, 38) type mismatch;
found : List[R]
required: List[O]
def r[O <: P] (): List[O] = testF(l)
My intuition suggests that this should have worked because T has a tighter upper type bound than O.
**** EDIT ****
def findNN[A, B <: A, C <: A, T] (seq: Seq[B], n: Int, center: C, distance: (A, A) => T)
(implicit ord: Ordering[T]): Seq[B] = {
import ord._
val ds = seq map ( (a: A) => distance(a, center))
val uds = ds.distinct
//#TODO: replace quickSelect with median-of algorithm if necessary
val kel = quickSelect(uds, n)
val z = seq zip ds
val (left, _) = z partition Function.tupled((_, d: T) => d <= kel)
left map {t => t._1}
}
OK, let's have a look at the example above.
The superclass A provides the method distance.
I would like to use the function findNN on a seq[B] having a center in a class C. distance should work because it works on A
Based on feedback provided, there's no way to simplify the type signatures above.
You made misprinting, you need >: rather then :<
class P(name: String)
class E(_name: String, role: String) extends P(_name)
def testF[T >: P](): List[T] = List(new P("Henry"))
You are trying to limit the type of the result using a type parameter R (with an upper bound type E), while you are not using the type R in your function.
An example of a correct use of a type parameter (with an upper bound):
def testF[T <: P](list: List[T]): List[T] = list
testF(List(new P("Tom")))
// List[P] = List(P#43bc21f0)
testF(List(new E("Jerry", "Mouse")))
// List[E] = List(E#341c1e65)
An incorrect use of a type parameter:
// does not compile, what is A ??
def foo[A]: List[A] = List("bar")