LabelledGeneric to get class name - scala

I'm fairly new to Shapeless, as one will infer from my question. Given an instance of LabelledGeneric, how do I get the name of the class that it represents. I can get the field name information from Keys, so I assume I need some other kind of Witness that encapsulates the type itself, but I cannot figure out which.
Eg, if I have a case class called Foo in package com.bar, I want to get the string "com.bar.Foo" (or separately is fine).
implicit def example[T, Repr <: HList](implicit label: LabelledGeneric.Aux[T, Repr],
kk: Keys[Repr]): Bibble[T] = new Bibble[T] {
override def typeName(value: T): String = ???
}

Shapeless's Generic provides a sum-of-products representation for case classes and sealed traits, which means that if we have a simple ADT like this:
sealed trait Base
case object Foo extends Base
case class Bar(i: Int, s: String) extends Base
Then Generic[Base] will give us a mapping to a Foo.type :+: Bar :+: CNil—i.e. a Foo.type or a Bar (where the or means we're talking about a "sum type" in type theoretic terms), and a Generic[Bar] gives us a mapping to an Int :: String :: HNil, which is Int and a String (a product type, where "product" has roughly the same meaning that it does in the case of the scala.ProductN types in the standard library).
LabelledGeneric uses an enhanced version of the sum-of-products representation, where each of the terms in the product or sum is tagged with a label. In the case of a sealed trait, these will be the constructor names for each subtype, and in the case of a case class they'll be the member names. These aren't fully-qualified names—just labels that disambiguate locally.
Generic and LabelledGeneric aren't intended to serve as general-purpose tools for compile-time reflection. They're not available for arbitrary types, for example, and they don't provide access to the name of the type itself.
Your best bet is probably to use TypeTag, but if you want a type-level representation of the name (like LabelledGeneric provides for labels), you'll need to define your own type class with macro-generated instances. Something like the following should work:
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
trait TypeInfo[A] { type Name <: String; def name: String }
object TypeInfo {
type Aux[A, Name0 <: String] = TypeInfo[A] { type Name = Name0 }
def apply[A](implicit ti: TypeInfo[A]): Aux[A, ti.Name] = ti
implicit def materializeTypeInfo[A, Name <: String]: Aux[A, Name] =
macro matTypeInfoImpl[A, Name]
def matTypeInfoImpl[A: c.WeakTypeTag, Name <: String](c: Context): c.Tree = {
import c.universe._
val A = c.weakTypeOf[A]
val name = A.typeSymbol.name.decodedName.toString.trim
q"new TypeInfo[$A] { type Name = ${ Constant(name) }; def name = $name }"
}
}
This is probably overkill for your use case, though, if you only need value-level strings.

Related

Scala type constraint precedence in implicit resolution

I have these 2 implicits
trait A[T] {
val name: String
}
trait B
object A {
implicit def product[T <: Product] = new A[T] {
override val name: String = "product"
}
implicit def childOfB[T <: Product with B] = new A[T] {
override val name: String = "child of B"
}
}
and if I try to find an implicit instance of A[C] where C is
case class C() extends B
childOfB will be selected.
I know it is logical but why does this happen? I cannot find it documented anywhere.
Scala Language Specification says:
If there are several eligible arguments which match the implicit
parameter's type, a most specific one will be chosen using the rules
of static overloading resolution.
Overloading resolution has a notion of one symbol being more specific than other. Precise, general definition of specificity is quite complex (as you can see in the specification linked above) but in your case it boils down to the fact that childOfB covers strictly a subset of types covered by product and therefore is more specific.
Specification says
If there are several eligible arguments which match the implicit
parameter's type, a most specific one will be chosen using the rules
of static overloading resolution.
To extend #ghik's answer, from Programmming in Scala,
To be more precise, one implicit conversion is more specific than another if one of the following applies:
The argument of the former is a subtype of the latter's
Both conversions are methods, and the enclosing class of the former extends the enclosing class of the latter
My guess is that "argument" in that quote refers also to type parameters, as is sugggested by
object A {
implicit def A2Z[T <: A] = new Z[T] {println("A")}
implicit def B2Z[T <: B] = new Z[T] {println("B")}
}
trait A
trait B extends A
trait Z[T]
def aMethod[T <: A](implicit o: Z[T]) = ()
implicit val a: A = new A {}
aMethod // prints B even though we started with an A

Bounds for type parameter of FunctionK

I'm using cats FreeMonad. Here's a simplified version of the algebra:
sealed trait Op[A]
object Op {
final case class Get[T](name: String) extends Op[T]
type OpF[A] = Free[Op, A]
def get[T](name: String): OpF[T] = liftF[Op, T](Get[T](name))
}
One of the interpreters will be a wrapper around a third-party library, called Client here which its get method's signature is similar to:
class Client {
def get[O <: Resource](name: String)
(implicit f: Format[O], d: Definition[O]): Future[O] = ???
}
My question is how can I encode that requirement in my implementation?
class FutureOp extends (Op ~> Future) {
val client = new Client()
def apply[A](fa: Op[A]): Future[A] =
fa match {
case Get(name: String) =>
client.get[A](name)
}
}
I tried things like introducing bounds to my apply (like apply[A <: Resource : Format : Definition]) which didn't work.
I understand that FunctionK is to transform values of first-order-kinded types, but is there anyway in which I can encode the requirements of the type parameter?
I intend to use it like:
def run[F[_]: Monad, A](intp: Op ~> F, op: OpF[A]): F[A] = op.foldMap(intp)
val p: Op.OpF[Foo] = Op.get[Foo]("foo")
val i = new FutureOp()
run(i, d)
(My original answer contained the same idea, but apparently it did not provide enough implementation details. This time, I wrote a more detailed step-by-step guide with a discussion of each intermediate step. Every section contains a separate compilable code snippet.)
TL;DR
Implicits are required for each type T that occurs in get[T], therefore they must be inserted and stored when the DSL-program is constructed, not when it is executed. This solves the problem with the implicits.
There is a general strategy for gluing a natural transformation ~> from several restricted natural transformations trait RNT[R, F[_ <: R], G[_]]{ def apply[A <: R](x: F[A]): G[A] } using pattern matching. This solves the problem with the A <: Resource type bound. Details below.
In your question, you have two separate problems:
implicit Format and Definition
<: Resource-type bound
I want to treat each of these two problems in isolation, and provide a reusable solution strategy for both. I will then apply both strategies to your problem.
My answer below is structured as follows:
First, I will summarize your question as I understand it.
Then I will explain what to do with the implicits, ignoring the type bound.
Then I will deal with the type bound, this time ignoring the implicits.
Finally, I apply both strategies to your particular problem.
Henceforth, I assume that you have scalaVersion 2.12.4, the dependencies
libraryDependencies += "org.typelevel" %% "cats-core" % "1.0.1"
libraryDependencies += "org.typelevel" %% "cats-free" % "1.0.1"
and that you insert
import scala.language.higherKinds
where appropriate.
Note that the solution strategies are not specific to this particular scala version or the cats library.
The setup
The goal of this section is to make sure that I'm solving the right problem, and also to provide very simple mock-up definitions
of Resource, Format, Client etc., so that this answer is self-contained
and compilable.
I assume that you want to build a little domain specific language using the Free monad.
Ideally, you would like to have a DSL that looks approximately like this (I've used the names DslOp for the operations and Dsl for the generated free monad):
import cats.free.Free
import cats.free.Free.liftF
sealed trait DslOp[A]
case class Get[A](name: String) extends DslOp[A]
type Dsl[A] = Free[DslOp, A]
def get[A](name: String): Dsl[A] = liftF[DslOp, A](Get[A](name))
It defines a single command get that can get objects of type A given a string
name.
Later, you want to interpret this DSL using a get method provided by some Client
that you cannot modify:
import scala.concurrent.Future
trait Resource
trait Format[A <: Resource]
trait Definition[A <: Resource]
object Client {
def get[A <: Resource](name: String)
(implicit f: Format[A], d: Definition[A]): Future[A] = ???
}
Your problem is that the get method of the Client has a type bound, and that
it requires additional implicits.
Dealing with implicits when defining interpreter for the Free monad
Let's first pretend that the get-method in client requires implicits, but
ignore the type bound for now:
import scala.concurrent.Future
trait Format[A]
trait Definition[A]
object Client {
def get[A](name: String)(implicit f: Format[A], d: Definition[A])
: Future[A] = ???
}
Before we write down the solution, let's briefly discuss why you cannot supply all
the necessary implicits when you are calling the apply method in ~>.
When passed to foldMap, the apply of FunctionK is supposed
to be able to cope with arbitrarily long programs of type Dsl[X] to produce Future[X].
Arbitrarily long programs of type Dsl[X] can contain an unlimited number of
get[T1], ..., get[Tn] commands for different types T1, ..., Tn.
For each of those T1, ..., Tn, you have to get a Format[T_i] and Definition[T_i] somewhere.
These implicit arguments must be supplied by the compiler.
When you interpret the entire program of type Dsl[X], only the type X but not the types T1, ..., Tn are available,
so the compiler cannot insert all the necessary Definitions and Formats at the call site.
Therefore, all the Definitions and Formats must be supplied as implicit parameters to get[T_i]
when you are constructing the Dsl-program, not when you are interpreting it.
The solution is to add Format[A] and Definition[A] as members to the Get[A] case class,
and make the definition of get[A] with lift[DslOp, A] accept these two additional implicit
parameters:
import cats.free.Free
import cats.free.Free.liftF
import cats.~>
sealed trait DslOp[A]
case class Get[A](name: String, f: Format[A], d: Definition[A])
extends DslOp[A]
type Dsl[A] = Free[DslOp, A]
def get[A](name: String)(implicit f: Format[A], d: Definition[A])
: Dsl[A] = liftF[DslOp, A](Get[A](name, f, d))
Now, we can define the first approximation of the ~>-interpreter, which at least
can cope with the implicits:
val clientInterpreter_1: (DslOp ~> Future) = new (DslOp ~> Future) {
def apply[A](op: DslOp[A]): Future[A] = op match {
case Get(name, f, d) => Client.get(name)(f, d)
}
}
Type bounds in case classes defining the DSL-operations
Now, let's deal with the type bound in isolation. Suppose that your Client
doesn't need any implicits, but imposes an additional bound on A:
import scala.concurrent.Future
trait Resource
object Client {
def get[A <: Resource](name: String): Future[A] = ???
}
If you tried to write down the clientInterpreter in the same way as in the
previous example, you would notice that the type A is too general, and that
you therefore cannot work with the contents of Get[A] in Client.get.
Instead, you have to find a scope where the additional type information A <: Resource
is not lost. One way to achieve it is to define an accept method on Get itself.
Instead of a completely general natural transformation ~>, this accept method will
be able to work with natural transformations with restricted domain.
Here is a trait to model that:
trait RestrictedNat[R, F[_ <: R], G[_]] {
def apply[A <: R](fa: F[A]): G[A]
}
It looks almost like ~>, but with an additional A <: R restriction. Now we
can define accept in Get:
import cats.free.Free
import cats.free.Free.liftF
import cats.~>
sealed trait DslOp[A]
case class Get[A <: Resource](name: String) extends DslOp[A] {
def accept[G[_]](f: RestrictedNat[Resource, Get, G]): G[A] = f(this)
}
type Dsl[A] = Free[DslOp, A]
def get[A <: Resource](name: String): Dsl[A] =
liftF[DslOp, A](Get[A](name))
and write down the second approximation of our interpreter, without any
nasty type-casts:
val clientInterpreter_2: (DslOp ~> Future) = new (DslOp ~> Future) {
def apply[A](op: DslOp[A]): Future[A] = op match {
case g # Get(name) => {
val f = new RestrictedNat[Resource, Get, Future] {
def apply[X <: Resource](g: Get[X]): Future[X] = Client.get(g.name)
}
g.accept(f)
}
}
}
This idea can be generalized to an arbitrary number of type constructors Get_1, ...,
Get_N, with type restrictions R1, ..., RN. The general idea corresponds to
the construction of a piecewise defined natural transformation from smaller
pieces that work only on certain subtypes.
Applying both solution strategies to your problem
Now we can combine the two general strategies into one solution for
your concrete problem:
import scala.concurrent.Future
import cats.free.Free
import cats.free.Free.liftF
import cats.~>
// Client-definition with both obstacles: implicits + type bound
trait Resource
trait Format[A <: Resource]
trait Definition[A <: Resource]
object Client {
def get[A <: Resource](name: String)
(implicit fmt: Format[A], dfn: Definition[A])
: Future[A] = ???
}
// Solution:
trait RestrictedNat[R, F[_ <: R], G[_]] {
def apply[A <: R](fa: F[A]): G[A]
}
sealed trait DslOp[A]
case class Get[A <: Resource](
name: String,
fmt: Format[A],
dfn: Definition[A]
) extends DslOp[A] {
def accept[G[_]](f: RestrictedNat[Resource, Get, G]): G[A] = f(this)
}
type Dsl[A] = Free[DslOp, A]
def get[A <: Resource]
(name: String)
(implicit fmt: Format[A], dfn: Definition[A])
: Dsl[A] = liftF[DslOp, A](Get[A](name, fmt, dfn))
val clientInterpreter_3: (DslOp ~> Future) = new (DslOp ~> Future) {
def apply[A](op: DslOp[A]): Future[A] = op match {
case g: Get[A] => {
val f = new RestrictedNat[Resource, Get, Future] {
def apply[X <: Resource](g: Get[X]): Future[X] =
Client.get(g.name)(g.fmt, g.dfn)
}
g.accept(f)
}
}
}
Now, the clientInterpreter_3 can cope with both problems: the type-bound-problem is dealt with
by defining a RestrictedNat for each case class that imposes an upper bound on its type arguments,
and the implicits-problem is solved by adding an implicit parameter list to DSL's get-method.
I think I've found a way to solve your problem by combining a ReaderT monad transformer with intersection types:
import scala.concurrent.Future
import cats.~>
import cats.data.ReaderT
import cats.free.Free
object FreeMonads {
sealed trait Op[A]
object Op {
final case class Get[T](name: String) extends Op[T]
type OpF[A] = Free[Op, A]
def get[T](name: String): OpF[T] = Free.liftF[Op, T](Get[T](name))
}
trait Resource
trait Format[A]
trait Definition[A]
trait Client {
def get[O <: Resource](name: String)
(implicit f: Format[O], d: Definition[O]): Future[O]
}
type Result[A] = ReaderT[
Future,
(Format[A with Resource], Definition[A with Resource]),
A,
]
class FutureOp(client: Client) extends (Op ~> Result) {
def apply[A](fa: Op[A]): Result[A] =
fa match {
case Op.Get(name: String) =>
ReaderT {
case (format, definition) =>
// The `Future[A]` type ascription makes Intellij IDEA's type
// checker accept the code.
client.get(name)(format, definition): Future[A]
}
}
}
}
The basic idea behind it is that you produce a Reader from your Op and that Reader receives the values that you can use for the implicit params. This solves the problem of type O having instances for Format and Definition.
The other problem is for O be a subtype of Resource. To solve this, we're just saying that the Format and Definition instances are not just instances of any A, but any A that also happens to be a Resource.
Let me know if you bump into problems when using FutureOp.

Relationship between parameterized class with different type, Scala

In Scala,
What is the relationship between parameterized class with different type?
For example,
class A[T]
What is the relationship between A[Int] (or A[String]) and A?
and what is the relationship between A[Int] and A[String] ?
I want to know, because I would like to do something like
case class A[T](v: T) { def print = println(v) }
def iter(s: Seq[A[???]]) = s.map(print) // What is the proper annotation of '???'
iter(Seq(A(1), A("Hello")) // 1 Hello
What is the relationship between A[Int] and A?
A is a type constructor of kind * → *, and A[Int] is one of the possible results of apply a type (Int) to the type constructor A.
What is the relationship between A[Int] and A[String]?
The least upper bound between these two types is A[_ >: String with Int], which can only be instantiated to A[Any] since it's the only super class of String and Int.
What is the proper annotation of '???'
In your example that would be Any, or a type parameter you could add to your def iter that would itself be instantiated to Any.
Types are related using Variance.
To answer your question, Java's wildcards equivalent in Scala is Existential types. So you can specify something like this:
def iter(s: Seq[A[_]]) = s.map(_.print)
which is equivalent to:
def iter(s: Seq[A[T] forSome {type T}]) = s.map(_.print)
You can also specify bounds like below:
def iter(s: Seq[A[_ <: CharSequence]]) = s.map(_.print)
println(iter(Seq(A[StringBuilder](new StringBuilder("Test")), A[String]("Hello"))))
[Explicit type specified in constructors to avoid implicit conversion to required type CharSequence].
Note that the following would not compile though:
def iter(s: Seq[A[CharSequence]]) = s.map(_.print)
println(iter(Seq(A[StringBuilder](new StringBuilder("Test")), A[String]("Hello"))))
This is where variance specification in class would help:
case class A[+T](v: T) {
def print = v
}
def iter(s: Seq[A[CharSequence]]) = s.map(_.print)
println(iter(Seq(A[StringBuilder](new StringBuilder("Test")), A[String]("Hello"))))

How to generically update a case class field using LabelledGeneric?

Using shapeless, one can use LabelledGeneric to update case class fields like so:
case class Test(id: Option[Long], name: String)
val test = Test(None, "Name")
val gen = LabelledGeneric[Test]
scala> gen.from(gen.to(test) + ('id ->> Option(1L)))
res0: Test = Test(Some(1),Name)
I would like the Test class (and others) to extend an abstract class Model, that will implement a method withId that would use a LabelledGeneric similar to the above code to update the id field, should it have one (which it should).
My attempt adds an implicit parameter of a LabelledGeneric[A] to the constructor of Model, which materializes just fine. I also need to somehow provide evidence to the record syntax that the LabelledGeneric#Repr has the id field to replace. Adding an implicit Updater parameter to withId satisfies the compiler, so that the code below will compile, but it is not usable.
import shapeless._, record._, ops.record._, labelled._, syntax.singleton._, tag._
abstract class Model[A](implicit gen: LabelledGeneric[A] { type Repr <: HList }) { this: A =>
def id: Option[Long]
val idWitness = Witness("id")
type F = FieldType[Symbol with Tagged[idWitness.T], Option[Long]]
def withId(id: Long)(implicit u: Updater.Aux[gen.Repr, F, gen.Repr]) =
gen.from(gen.to(this) + ('id ->> Option(id)))
}
case class Test(id: Option[Long], name: String) extends Model[Test]
When calling test.withId(1), the implicit Updater fails to materialize. The macro reports that gen.Repr isn't an HList type, when it in fact is. It seems that this match is the one that fails, where u baseType HConsSym returns <notype>. Equivalent to:
scala> weakTypeOf[test.gen.Repr].baseType(weakTypeOf[::[_, _]].typeConstructor.typeSymbol)
res12: reflect.runtime.universe.Type = <notype>
This is using shapeless 2.3, though it fails for different reasons in 2.2 (seems as though Updater had a large refactor).
Is it possible to accomplish this with shapeless, or am I way off target?
The main issue here is that the refined result type of the LabelledGeneric (Repr) is lost. At Model, the only thing known about Repr is Repr <: HList. The implicit Updater.Aux[gen.Repr, F, gen.Repr] searches for something that is only known as _ <: HList and thus fails to materialize.
You'd have to define Model with two type parameters
abstract class Model[A, L <: HList](implicit gen: LabelledGeneric.Aux[A, L])
but this doesn't allow you to write class Test extends Model[Test] and you have to write the labelled generic type by hand.
If you instead move the gen down to withId, you can make it work:
object Model {
private type IdField = Symbol with Tagged[Witness.`"id"`.T]
private val IdField = field[IdField]
type F = FieldType[IdField, Option[Long]]
}
abstract class Model[A] { this: A =>
import Model._
def id: Option[Long]
def withId[L <: HList](id: Long)(implicit // L captures the fully refined `Repr`
gen: LabelledGeneric.Aux[A, L], // <- in here ^
upd: Updater.Aux[L, F, L] // And can be used for the Updater
): A = {
val idf = IdField(Option(id))
gen.from(upd(gen.to(this), idf))
}
}
case class Test(id: Option[Long], name: String) extends Model[Test]
If you're concerned with resolution performance, you can cache the value(s) in the companion of Test:
case class Test(id: Option[Long], name: String) extends Model[Test]
object Test {
implicit val gen = LabelledGeneric[Test]
}
This would mean that code like this
val test = Test(None, "Name")
println("test.withId(12) = " + test.withId(12))
println("test.withId(12).withId(42) = " + test.withId(12).withId(42))
would use the definition of Test.gen instead of materializing a new LabelledGeneric every time.
This works for both, shapeless 2.2.x and 2.3.x.

Context bounds for generic polymorphic data in collection

I have the simplified situation:
abstract sealed trait Top
class A[T] extends Top
class B[T] extends Top
class Typeclass[T]
implicit def a[T] = new Typeclass[A[T]]
implicit def b[T] = new Typeclass[B[T]]
Now I have a Map[String, Top] and want to use an operation on all values in the map that require the presence of an instance of Typeclass to be available in the context. This will not compile as the concrete types of the values in the map are not visible from its type and I can therefore not set a context bound for them.
Is there a way to tell the compiler that in fact there will always be an instance available? In this example this is given as there are implicit functions to generate those instances for every concrete subtype of Top.
Or is the only solution to use a HList and recurse over its type requiring all the instances to be in context?
I recommend using some variation on this adaptation of Oleg's Existentials as universals in this sort of situation ... pack the the type class instance along with the value it's the instance for,
abstract sealed trait Top
class A[T] extends Top
class B[T] extends Top
class Typeclass[T]
implicit def a[T] = new Typeclass[A[T]]
implicit def b[T] = new Typeclass[B[T]]
trait Pack {
type T <: Top
val top: T
implicit val tc: Typeclass[T]
}
object Pack {
def apply[T0 <: Top](t0: T0)(implicit tc0: Typeclass[T0]): Pack =
new Pack { type T = T0 ; val top = t0 ; val tc = tc0 }
}
val m = Map("a" -> Pack(new A[Int]), "b" -> Pack(new B[Double]))
def foo[T: Typeclass](t: T): Unit = ()
def bar(m: Map[String, Pack], k: String): Unit =
m.get(k).map { pack =>
import pack._ // imports T, top and implicit tc
foo(top) // instance available for call of foo
}
bar(m, "a")
As discussed in comment it would be more convenient to have the typeclass defined on Top, and it might be done with pattern matching.
supposing part of the definition of the typeclass is
def f[T](t: T): FResult[T],
and you have the corresponding implentations
def fOnA[T](t: A[T]): FResult[A[T]] = ...
def fOnB[T](t: B[T]): FResult[B[T]] = ...
Then you can define
def fOnT(t: Top) : FResult[Top] = t match {
case a: A[_] => fOnA(a)
// provided an FResult[A[T]] is an FResult[Top],
// or some conversion is possible
case b: B[_] => fOnB(b)
}
If is both legal and safe to call a generic method, such as fOnA[T] with an existential (a matching A[_])
However, it might be difficult to convince the compiler that the parameter you pass to f or the result you get are ok, given the reduced information of the existential. If so, please post the signatures you need.