If I have a method such as:
def f[T: Generic, U: Generic](t: T): U
Generic[T].to(t) returns type Generic[T]#Repr which I assume is a type alias for some type of HList.
Is it possible to select members from the HList and build another HList which I can convince the compiler is of type Generic[U]#Repr which I can then use to create an instance of U using Generic[U].from(myNewHList)?
I have tried many approaches but seem to be going around in circles.
When doing stuff like this in Shapeless, the best place to look is in the shapeless.ops typeclasses. In this case, since you know your second class is a strict subset of your first, Intersection is sufficient to get what you want. You'll want to set this up as a type class, so that you can pass in your input and output types and let the compiler infer the intermediate stuff.
trait Converter[A,B] {
def convert(a: A): B
}
object Converter {
def apply[A,B](implicit converter: Converter[A,B]) = converter
implicit def genericConverter[A, B, ARepr <: HList, BRepr <: HList](
implicit
genA: Generic.Aux[A,ARepr],
genB: Generic.Aux[B,BRepr],
intersection: shapeless.ops.hlist.Intersection.Aux[ARepr,BRepr,BRepr]
): Converter[A,B] =
new Converter[A,B]{def convert(a: A): B = genB.from(intersection(genA.to(a)))}
}
This can be used as follows:
case class Foo(a: Int, b: Int, c: String)
case class Bar(a: Int, c: String)
val foo = Foo(1,2,"Three")
val bar: Bar = Converter[Foo, Bar].convert(foo)
Related
Is there a way to derive a type from an existing one in Scala?
For example, for case class Person(name: String, age: Int) I'd like to get a Product/Tuple of (Option[String], Option[Int]), i.e. a type mapped from an existing one.
There's a feature in Typescript (mapped types) that allows this relatively easily, which is how I started thinking down this path. But I'm not sure how something like this would be done in Scala.
I feel like the solution involves using shapeless in some way but I'm not sure how to get there.
I'd suggest parameterize the type as follows:
case class Person[F[_]](name: F[String], age: F[Int])
And then you can derive types you want, like
import cats.Id
type IdPerson = Person[Id]
type OptPerson = Person[Option]
Where cats.Id is simply defined as type Id[A] = A. It is straightforward to write your own, but I suggest using cats' one since it comes with useful typeclass instances.
With Shapeless you can define type class
import shapeless.ops.{hlist, product, tuple}
import shapeless.poly.~>
import shapeless.{Generic, HList, Id, the}
trait Partial[A] {
type Out
}
object Partial {
type Aux[A, Out0] = Partial[A] { type Out = Out0 }
object optionPoly extends (Id ~> Option) {
override def apply[T](t: T): Option[T] = null
}
// implicit def mkPartial[A, L <: HList, L1 <: HList](implicit
// generic: Generic.Aux[A, L],
// mapper: hlist.Mapper.Aux[optionPoly.type, L, L1],
// tupler: hlist.Tupler[L1]): Aux[A, tupler.Out] = null
implicit def mkPartial[A, T](implicit
toTuple: product.ToTuple.Aux[A, T],
mapper: tuple.Mapper[T, optionPoly.type],
): Aux[A, mapper.Out] = null
}
and use it (the is improved version of implicitly)
case class Person(name: String, age: Int)
// val pp = the[Partial[Person]]
// type PersonPartial = pp.Out
type PersonPartial = the.`Partial[Person]`.Out
implicitly[PersonPartial =:= (Option[String], Option[Int])]
This is a subquestion from a post I made earlier today.
I am working with HList and Generic hoping to perform some implicit conversion between case classes.
Consider:
case class A(a: Int, b: Double, c: Boolean)
case class B(b: Double, c: Boolean)
I can convert between the two types very simply:
Generic[B].from(Generic[A].to(A(1, 2.0, true)).tail) // B(2.0, true)
I want to be able to pimp a method onto Product so I can do the following:
A(1, 2.0, true).to[B]
My attempt is as follows:
object ShapelessTest extends App {
implicit class convert[From <: Product, FromHListT <: HList]
(x: From)(implicit genFrom: Generic.Aux[From, FromHlistT]){
def to[To <: Product, ToHlistT <: HList]
(implicit genTo: Generic.Aux[To, ToHilstT]): To = {
val fromHList = genFrom.to(x)
val toHList = ??? // Manipulate toHList to correspond with type ToHListT
genTo.from(toHList)
}
}
println(C(1, 2.0, true).to[D, Double :: Boolean :: HNil])
}
Now my code is less than ideal for two reasons.
I have to explicitly supply the Type of the To HList.
I need to manipulate the FromHListT instance (perhaps selecting values by index) and then ensure it is of Type ToHListT.
Does anyone have any advice how I can obtain Generic.Aux[T, ToHListT] without having to specify a second type parameter?
Can anyone advise how I can manipulate (remove items from, reorder FromHListT) and convert to instance of ToHListT.
You should read chapter "6.3 Case study: case class migrations" of "The Type Astronaut’s Guide to Shapeless"
https://books.underscore.io/shapeless-guide/shapeless-guide.html#sec:ops:migration
Basically when you would like to hide extra generics you introduce a type class.
Also instead of implicit g: Generic.Aux[A, B] you can try to use implicit g: Generic[A] and type g.Repr instead of B.
Normally breakout would aid in conversion from one collection to another, but it doesn't seem to be able to infer the necessary colleciton constuctor for C:
import scala.collection.breakOut
object Utils {
implicit class IterableExtra[T, C[X] <: Iterable[X]](val list: C[T]) extends AnyVal {
def empty: C[T] = Iterable.empty[T].map(x => x)(breakOut)
}
}
Ideally this would work with minimal reflection, so that it might work in scala.js
Update I was also trying to use this in a different way, and I forgot to have the implicit at the outermost level:
def testIterableEmpty[B, I[X] <: Iterable[X]](implicit cbf: CanBuildFrom[I[B], B, I[B]]): I[B] = {
def emptyIter: I[B] = cbf().result()
emptyIter
}
scala> val x: List[Int] = testIterableEmpty[Int, List]
x: List[Int] = List()
breakOut is defined like so:
def breakOut[From, T, To](implicit b: CanBuildFrom[Nothing, T, To]): CanBuildFrom[From, T, To]
So it cannot be used to avoid passing a CanBuildFrom into your empty method - it requires one itself. Luckily, it is easy to write - you want to create a C[T] out of C[T], and the element type is T, so:
def empty(implicit cbf: CanBuildFrom[C[T], T, C[T]]): C[T] =
Iterable.empty[T].map(x => x)(breakOut)
Tho since you have a CanBuildFrom instance anyway, the implementation using it directly is straightforward too:
def empty(implicit cbf: CanBuildFrom[C[T], T, C[T]]): C[T] =
cbf().result()
I am trying to understand typeclasses and so far i got to Monoids, which are simple enough:
object AbstractAlgebra {
case class Pair[A,B](a: A, b: B)
trait Monoid[T] {
def times(t1: T, t2: T): T
def unit: T
}
object Monoid {
implicit object MonoidIntPlus extends Monoid[Int] {
def times(i1: Int, i2: Int) = i1 + i2
def unit = 0
}
implicit object StringMonoid extends Monoid[String] {
def times(s1: String, s2: String) = s1 + s2
def unit = ""
}
implicit object PairOfMonoids extends Monoid[Pair[Monoid, Monoid]] = ???
}
}
I suppose my problem is the type Monoid[Pair[Monoid, Monoid]], cause I'm not really dealing with two monoid instances, only two classes that are implicit monoids, but I am not sure how to express that.
Any help or references would be appreciated
Monoid is not a type in itself. It's a type constructor, so Pair[Monoid, Monoid] makes no sense.
What you really want is actually the following: assuming that you have a Monoid type class instance for two given types A and B, then make an instance also for Pair[A, B].
This can be written as follows (the implementation is the most natural one you can derive):
implicit def monoidPair[A, B](implicit A: Monoid[A], B: Monoid[B]): Monoid[Pair[A, B]] = new Monoid[Pair[A, B]] {
def times(p1: Pair[A, B], p2: Pair[A, B]) =
Pair(A.times(p1.a, p2.a), B.times(p1.b, p2.b))
def unit = Pair(A.unit, B.unit)
}
This will do exactly what I explained before: If implicit instances for types Monoid[A] and Monoid[B] are found, then it puts a new implicit instance of type Monoid[Pair[A, B]] in scope.
Note. Your case class Pair[A, B] is already defined in Predef (although it's been deprecated since 2.11.0) as Tuple2[A, B] or (A, B).
Other note. If you don't like defining implicit instances as def or val, you can do the same with an implicit class:
implicit class MonoidPair[A, B](implicit A: Monoid[A], B: Monoid[B]) extends Monoid[Pair[A, B]] {
... //same body as before
}
I'm trying to get the hang of Scala traits and case classes. Below is a followup to this question.
Suppose I have a simple class and an object that extends it.
sealed trait Operations{
def add(a:Double,b:Double):Double
def multiply(a:Double,b:Double):Double
}
case object CorrectOperations extends Operations{
def add(a:Double,b:Double):Double = a+b
def multiply(a:Double,b:Double):Double= a*b
}
Now I have some function that will make use of any object of type Operations, such as,
def doOperations(a:Double,b:Double, op:Operations)={ op.multiply(a,b) - op.add(a,b)}.
This works well, but my question is how to generalize the types of trait Operations, so we're not just talking about Doubles. So i'd like to have generic types for trait Operations and then type specification for each object.
Using type generics, I tried
sealed trait Operations[T]{
def add(a:T,b:T):T
def multiply(a:T,b:T):T
}
case object CorrectOperations extends Operations[Double]{
def add(a:Double,b:Double):Double = a+b
def multiply(a:Double,b:Double):Double= a*b
}
def doOperations[T](a:T,b:T, op:Operations[T])={ op.multiply(a,b) - op.add(a,b) },
with a compile error at doOperations - "value - is not a member of type parameter T".
So we know that op.multiply(a,b) will return type T, and the error would indicate that type T has no .- method.
How should I be thinking about achieving this generalization of trait Operations ? Thanks
In the context of your problem, you should introduce a subtract method into your Operations trait, so that you can provide evidence that T has such a method (well it doesn't, but a method that does a subtraction from to T from another).
sealed trait Operations[T] {
def add(a: T, b: T): T
def multiply(a: T, b: T): T
def subtract(a: T, b: T): T
}
case object CorrectOperations extends Operations[Double]{
def add(a: Double, b: Double): Double = a + b
def multiply(a: Double, b: Double): Double = a * b
def subtract(a: Double, b: Double): Double = a - b
}
def doOperations[T](a: T, b: T, op: Operations[T]) =
op.subtract(op.multiply(a,b), op.add(a,b))
This is basically what the Numeric trait does.
The problem you are running into is that there is no - (minus) operation in your type class to subtracting your multiply result from your add result fruitlessly looks for that operator on type T.
Try adding minus to your type class:
sealed trait Operations[T]{
def add(a:T,b:T):T
def multiply(a:T,b:T):T
def minus(a:T,b:T):T
}
def doOperations[T](a:T,b:T, op:Operations[T])=
op.minus(op.multiply(a,b) - op.add(a,b))