Generic field accessor powered by Shapeless - scala

I'm trying to implement a convenient generic field accessor based on LabelledGeneric.
The usage should look like:
case class Foo(aha: String, uhu: Double, ehe: Int)
case class Bar(uhu: Double, ahu: Boolean)
val foo: Foo = ???
val bar: Bar = ???
val uhuGenField = new GenField('uhu)
val uhuFooAccess = uhuGenField.from[Foo]
val uhuBarAccess = uhuGenField.from[Bar]
def someFunWithUhu[X](xs: Seq[X], access: uhuGenField.Access[X]) = ???
I spent some time trying to figure out how to achieve such behaviour.
Eventually I came up with this approach:
import shapeless._
import shapeless.ops.record.Selector
final class GenField[V](val fieldName: Symbol) {
val fieldWitness = Witness(fieldName)
type FieldNameType = fieldWitness.T
trait Access[C] {
def get(c: C): V
}
def from[C](implicit lg2hl: LGtoHL[C]): Access[C] = new Access[C] {
override def get(c: C): V = {
val labelledGeneric = lg2hl.labelledGeneric
val selector = Selector.mkSelector[labelledGeneric.Repr, FieldNameType, V]
selector(labelledGeneric.to(c))
}
}
}
// I need something like this to enable syntax like
// genField.from[DesiredClass]
// i.e. to "latch" Repr inside a single instance
// and to don't pass it explicitly to `from` method.
sealed trait LGtoHL[A] {
type Repr <: HList
val labelledGeneric: LabelledGeneric.Aux[A, Repr]
}
object LGtoHL {
implicit def mkLGtoHL[A, ARepr <: HList](implicit lg: LabelledGeneric.Aux[A, ARepr]): LGtoHL[A] = {
new LGtoHL[A] {
override type Repr = ARepr
override val labelledGeneric: LabelledGeneric.Aux[A, Repr] = lg
}
}
}
From my prospective this solution should be OK, but it still doesn't work.
The compilation fails with the following error message:
Error:(17, 41) lg2hl.Repr is not an HList type
val selector = Selector.mkSelector[labelledGeneric.Repr, FieldNameType, V]
Why does it complain lg2hl.Repr is not an HList type?
Repr is explicitly defined in LGtoHL as type Repr <: HList.
What is wrong with my code?
Very appreciate your help.

Why are lenses not enough?
import shapeless.{Lens, lens}
case class Foo(aha: String, uhu: Double, ehe: Int)
case class Bar(uhu: Double, ahu: Boolean)
val foo: Foo = Foo("a", 1.0, 2)
val bar: Bar = Bar(3.0, true)
val fooUhu: Lens[Foo, Double] = lens[Foo] >> 'uhu
val barUhu: Lens[Bar, Double] = lens[Bar] >> 'uhu
fooUhu.get(foo) // 1.0
barUhu.get(bar) // 3.0
The error message
lg2hl.Repr is not an HList type
comes from here: https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/generic.scala#L511
u.baseType(HConsSym) is now NoType.
I guess GenField[V](val fieldName: Symbol) will not work since fieldName in Witness(fieldName) must be known at compile time. For example
lens[Foo] >> 'uhu
works but
val uhu: Witness.`'uhu`.T = 'uhu.narrow
lens[Foo] >> uhu
doesn't. This is the reason why lenses, Witness, LabelledGeneric are implemented via macros.

Related

Is there a way to use .apply/.unapply on Generic objects?

I am using a Generic Mapper to map a Model from a DBModel in scala 2.11
it has a method defined as:
def fromModelToDBModel(m: T) : R
where T is the type of the Model and R is the type of the DBModel.
This method is implemented the same way in almost all inheriting objects, ie:
override def fromModelToDBModel(p: RawModel): RawDBModelV1 = {
val values = RawModel.unapply(p).get
val makeDBModel = (RawDBModelV1.apply _).tupled
makeDBModel(values)
}
Is it possible to somehow define a generic object in the base Trait, so I can call apply/unapply on it?
example of the logic I would like to implement:
def fromModelToDBModel(m: T) : R = {
val values = Object[T].unapply(p).get
val makeDBModel = (Object[R].apply _).tupled
makeDBModel(values)
}
This would eliminate the need for writing every mapper individually, making the code more DRY.
You can look into generic programming with Shapeless.
With Shapeless, your function could be written as:
import shapeless._
def fromModelToDBModel[In, Out, Repr <: HList](p: In)(implicit genI: Generic.Aux[In, Repr], genO: Generic.Aux[Out, Repr]): Out = {
val values = genI.to(p)
val makeDBModel = genO.from _
makeDBModel(values)
}
case class RawModel(a: Int, b: String, c: Boolean)
case class RawModelV1(x: Int, y: String, z: Boolean)
val rawModel = RawModel(42, "foo", true)
val rawModelV1 = fromModelToDBModel[RawModel, RawModelV1, Int :: String :: Boolean :: HNil](RawModel(42, "foo", true))
This can be a little unwieldy with the required explicit type params so might want to refer to this SO question for ways to avoid explicit type params. For example, with partial application:
def fromModelToDBModel2[B] = new PartiallyApplied[B]
class PartiallyApplied[B] {
def apply[A, Repr](a: A)(implicit genA: Generic.Aux[A, Repr], genB: Generic.Aux[B, Repr]) = genB.from(genA.to(a))
}
val rawModelV1_2 = fromModelToDBModel2[RawModelV1](rawModel)

How to restrict a method only to being used with a specific type?

Suppose I have a case class below
case class SomeCaseClass[M] private (
value: String
)
and in another file, I have the following trait and object.
trait SomeTrait[A] {
def get(oldId: String): A
:
}
object SomeObject {
private[this] def init[A](): SomeTrait[A] = new SomeTrait[A] {
def get(oldId: String): A = id(oldId)
:
}
val aaa: SomeTrait[String] = init[String]()
val bbb: SomeTrait[SomeCaseClass[String]] = init[SomeCaseClass[String]]()
}
How should I modify the code so that restrict the init method only to being used with SomeCaseClass[_] type and not with any types like String as above?
Ideally with some modification to the code, the line val aaa: SomeTrait[String] = init[String]() should cause compilation error.
This is what I came up with:
case class SomeCaseClass[M] private (
value: String
)
trait SomeTrait[A] {
def get(oldId: String): A
}
private[this] def init[A <: SomeCaseClass[_]](): SomeTrait[A] = new SomeTrait[A] {
def get(oldId: String): A = ???
}
val aaa: SomeTrait[String] = init[String]() // Will fail
val bbb: SomeTrait[SomeCaseClass[String]] = init[SomeCaseClass[String]]()
It fails with
ScalaFiddle.scala:16: error: type arguments [String] do not conform to method init's type parameter bounds [A <: ScalaFiddle.this.SomeCaseClass[_$1] forSome { type _$1 }]
You can check this scalafiddle.
I do not know if this is the best approach, but init[A <: SomeCaseClass[_]] is adding a type bound to A, and forcing A to be a Subclass of SomeCaseClass. I would love to know if there is a better way though.
You can force a type parameter to be equal to some type B by using an implicit parameter:
def foo[A](implicit e: A =:= B): …
Also see this question.
To add some more value to this answer.
Following code shows how to use the implicit parameter e: A =:= String to convert an A to a String.
def bar(b: String): Unit = println(b)
def foo[A](a: A)(implicit e: A =:= String): Unit = {
bar(e(a))
}
foo("hi") //compiles
foo(5) //error: Cannot prove that scala.this.Int =:= String.
Answer to problem the OP has
This problem is much simpler: Make the method parametric only in the parameter A of SomeCaseClass[A], instead of using the whole type SomeCaseClass[A] as a type parameter:
private[this] def init[A](): SomeTrait[SomeCaseClass[A]] = new
SomeTrait[SomeCaseClass[A]] {
def get(oldId: String): SomeCaseClass[A] = ???
}
This is based on the answer above:
case class SomeCaseClass[M] private (
value: String
)
trait SomeTrait[A] {
def get(oldId: String): SomeCaseClass[A]
}
private[this] def init[A](): SomeTrait[A] = new SomeTrait[A] {
def get(oldId: String): SomeCaseClass[A] = ???
}
val aaa: SomeTrait[String] = init[String]()
(https://scalafiddle.io/sf/KuXZc0h/3)
This doesn't allow other types than SomeCaseClass to be used with SomeTrait.

How convert from trait with type member to case class with type parameter an viceversa

I don't know how to solve a problem in scala. Maybe someone can help me!
I have a case class (Operation) with some type parameter, this class can be returned by a method that know nothing about the parameter types (example a parser from string/json/xml).
So I need a way to transform from ShadowedOperation to Operation in some way, because the need is to parse from some data a ShadowedOperation and from this extract the typed version (an Operation).
I've write a code that should express the problem, it's simplified and try to do something different, but if this can be solved I can solve also the real need.
Probably with shapeless there is a solution, but I need to find a solution without it.
object box {
trait Transform[A, B] {
def apply(in: A): B
}
object Transform {
def instance[A, B](f: A => B): Transform[A, B] = new Transform[A, B] {
override def apply(in: A): B = f(in)
}
}
implicit class TransformOps[T](w: T) {
def transform(implicit t: Transform[T, String]) = t(w)
}
trait ShadowedOperation {
type I
type O
def param: String
def otherParam: Int
def in: I
def out: O
}
object ShadowedOperation {
// How can i do this in a generic, typed and wonderful way ???
implicit def operationToString: Transform[ShadowedOperation, String] = ???
}
case class Operation[I0, O0](
param: String,
otherParam: Int,
in: I0,
out: O0
) extends ShadowedOperation {type I = I0; type O = O0}
object Operation {
implicit def operationToString[I, O](
implicit
iToString: Transform[I, String],
oToString: Transform[O, String]
): Transform[Operation[I, O], String] =
Transform.instance(in => s"${in.otherParam} - ${in.param} - ${iToString(in.in)} - ${oToString(in.out)}")
}
def fakeParseFromString(in: String): List[ShadowedOperation] = {
// this simulate a parsing (or read from db) from string to extract the case class
List(Operation("param", 0, "in!", "out!"), Operation("param", 0, "in!", 100))
}
}
object Main extends App {
import box._
implicit val intToString: Transform[Int, String] = Transform.instance(_.toString)
implicit val stringToString: Transform[String, String] = Transform.instance(_.toString)
val op = Operation("param", 0, "in!", "out!")
val shadowedOperationList = fakeParseFromString("imagine that this string contain a json")
val opString = op.transform
val shadowedOpString = shadowedOperationList.map(_.transform)
println(opString)
println(shadowedOpString)
}
Thanks in advance to all who can help!
I made several changes:
added covariance/contravariance to Transform[-A, +B]
introduced type ShadowedOperation.Aux[I0, O0]
fixed returning type of fakeParseFromString using Aux-type
lifted operationToString from companion object of case class to companion object of trait with corresponding changes
imported instance: import op._
The whole code:
object box {
trait Transform[-A, +B] {
def apply(in: A): B
}
object Transform {
def instance[A, B](f: A => B): Transform[A, B] = new Transform[A, B] {
override def apply(in: A): B = f(in)
}
}
implicit class TransformOps[T](w: T) {
def transform(implicit t: Transform[T, String]) = t(w)
}
trait ShadowedOperation {
type I
type O
def param: String
def otherParam: Int
def in: I
def out: O
implicit def operationToString(
implicit
iToString: Transform[I, String],
oToString: Transform[O, String]
): Transform[ShadowedOperation.Aux[I, O], String] =
Transform.instance(in => s"${in.otherParam} - ${in.param} - ${iToString(in.in)} - ${oToString(in.out)}")
}
object ShadowedOperation {
type Aux[I0, O0] = ShadowedOperation { type I = I0; type O = O0 }
}
case class Operation[I0, O0](
param: String,
otherParam: Int,
in: I0,
out: O0
) extends ShadowedOperation {type I = I0; type O = O0}
def fakeParseFromString[I, O](in: Operation[I, O]): ShadowedOperation.Aux[I, O] = in
}
def main(args: Array[String]): Unit = {
import box._
implicit val intToString: Transform[Int, String] = Transform.instance(_.toString)
implicit val stringToString: Transform[String, String] = Transform.instance(_.toString)
val op = Operation("param", 0, "in!", "out!")
val shadowedOperation = fakeParseFromString(op)
import op._
val opString = op.transform
val shadowedOpString = shadowedOperation.transform
println(opString)//0 - param - in! - out!
println(shadowedOpString)//0 - param - in! - out!
}
So shapeless isn't necessary here.
When you write just ShadowedOperation instead of ShadowedOperation.Aux[???, ???] you loose some information about types. You have to find a way to restore this information about I, O (some casting, specifying types explicitly, defining more implicits etc.). Otherwise implicits won't work.
For instance in your updated example you can write
def fakeParseFromString(in: String): List[ShadowedOperation.Aux[String, Any]] =
List(Operation("param", 0, "in!","out!"), Operation("param", 0, "in!", 100))
implicit val anyToString: Transform[Any, String] = Transform.instance(_.toString)
val shadowedOpString = shadowedOperationList.map(_.transform)
println(shadowedOpString)
// List(Operation(param,0,in!,out!), Operation(param,0,in!,100))

Mapping of String to Type

I am currently trying to write a method that takes a JSON (what API does not matter here) and validates it. I want the method to look something like this:
def validateJson(json, expectedType: Map[String, Type(?)], allowedVals: Map[String, Seq[expectedType(key)]]): Boolean
The problem is: I do have a method jsonfield.validate[expectedType] but I do not know how to pass an unknown number of useable type parameters associated with strings to a method.
I'd gladly use some runtime reflection if that is possible here, or any advanced feature necessary to make this work easily. Any suggestions appreciated.
PS: I am using Play Framework 2.6.3
Edit:
I am trying to use the passed types like this
val allowed = allowedVals(field) // a Set
// if field contents contained in allowed value set...
if( allowed(field.validate[expectedType(field)].get) ) foo
Maybe you can use varargs in runtime or abstract over arity in compile time or just use an HList:
def foo[L <: HList](l: L) = ???
trait A
trait B
trait C
val a: A = new A {}
val b: B = new B {}
val c: C = new C {}
foo[A :: B :: C :: HNil](a :: b :: c :: HNil)
Sounds like you're looking for dependent type/dependent function/polymorphic function:
import shapeless.Poly1
import shapeless.syntax.singleton._
object expectedTypeAndValue extends Poly1 {
implicit val aCase: Case.Aux["a", Int] = at["a"](_ => 1)
implicit val bCase: Case.Aux["b", Long] = at["b"](_ => 2L)
implicit val cCase: Case.Aux["c", Double] = at["c"](_ => 3.0)
}
def validateJson(json: Json): Boolean = {
val x: Long = expectedTypeAndValue["b"]("b".narrow)
???
}
in Typelevel Scala or
import shapeless.{Poly1, Witness}
import shapeless.syntax.singleton._
object expectedTypeAndValue extends Poly1 {
implicit val aCase: Case.Aux[Witness.`"a"`.T, Int] = at[Witness.`"a"`.T](_ => 1)
implicit val bCase: Case.Aux[Witness.`"b"`.T, Long] = at[Witness.`"b"`.T](_ => 2L)
implicit val cCase: Case.Aux[Witness.`"c"`.T, Double] = at[Witness.`"c"`.T](_ => 3.0)
}
def validateJson(json: Json): Boolean = {
val x: Long = expectedTypeAndValue[Witness.`"b"`.T]("b".narrow)
???
}
in Lightbend Scala (ordinary Scala).
You can create also custom type class:
trait ExpectedTypeAndVals[S <: String] {
type Out
def apply(s: S): Set[Out]
}
object ExpectedTypeAndVals {
type Aux[S <: String, Out0] = ExpectedTypeAndVals[S] {type Out = Out0}
implicit def mkExpectedTypeAndVals[S <: String]: ExpectedTypeAndVals.Aux[S, ???] =
new ExpectedTypeAndVals[S] {
override type Out = ???
override def apply(s: S): Set[Out] = ???
}
}
def allowed[S <: String, Out](json: Json)(implicit
typeAndVals: ExpectedTypeAndVals.Aux[S, Out]
): Boolean = {
val str: S = ???
val set: Set[Out] = typeAndVals(str)
???
}
if(allowed(json)) {
???
}

Constructing TypeTags of higher-kinded types

Given a simple parametrized type like class LK[A], I can write
// or simpler def tagLK[A: TypeTag] = typeTag[LK[A]]
def tagLK[A](implicit tA: TypeTag[A]) = typeTag[LK[A]]
tagLK[Int] == typeTag[LK[Int]] // true
Now I'd like to write an analogue for class HK[F[_], A]:
def tagHK[F[_], A](implicit ???) = typeTag[HK[F, A]]
// or some other implementation?
tagHK[Option, Int] == typeTag[HK[Option, Int]]
Is this possible? I've tried
def tagHK[F[_], A](implicit tF: TypeTag[F[_]], tA: TypeTag[A]) = typeTag[HK[F, A]]
def tagHK[F[_], A](implicit tF: TypeTag[F], tA: TypeTag[A]) = typeTag[HK[F, A]]
but neither works for the obvious reasons (in the first case F[_] is the existential type instead of the higher-kinded one, in the second TypeTag[F] doesn't compile).
I suspect the answer is "it's impossible", but would be very happy if it isn't.
EDIT: we currently use WeakTypeTags as follows (slightly simplified):
trait Element[A] {
val tag: WeakTypeTag[A]
// other irrelevant methods
}
// e.g.
def seqElement[A: Element]: Element[Seq[A]] = new Element[Seq[A]] {
val tag = {
implicit val tA = implicitly[Element[A]].tag
weakTypeTag[Seq[A]]
}
}
trait Container[F[_]] {
def lift[A: Element]: Element[F[A]]
// note that the bound is always satisfied, but we pass the
// tag explicitly when this is used
def tag[A: WeakTypeTag]: WeakTypeTag[F[A]]
}
val seqContainer: Container[Seq] = new Container[Seq] {
def lift[A: Element] = seqElement[A]
}
All of this works fine if we replace WeakTypeTag with TypeTag. Unfortunately, this doesn't:
class Free[F[_]: Container, A: Element]
def freeElement[F[_]: Container, A: Element] {
val tag = {
implicit val tA = implicitly[Element[A]].tag
// we need to get something like TypeTag[F] here
// which could be obtained from the implicit Container[F]
typeTag[Free[F, A]]
}
}
Does this serve your purposes?
def tagHK[F[_], A](implicit tt: TypeTag[HK[F, A]]) = tt
As opposed to using the implicit parameter to get the TypeTag for F and A separately and then composing them, you can directly request the tag you want from the compiler. This passes your test case as desired:
tagHK[Option, Int] == typeTag[HK[Option, Int]] //true
Alternatively, if you have an instance of TypeTag[A] you could try:
object HK {
def apply[F[_]] = new HKTypeProvider[F]
class HKTypeProvider[F[_]] {
def get[A](tt: TypeTag[A])(implicit hktt: TypeTag[HK[F, A]]) = hktt
}
}
Allowing you to do:
val myTT = typeTag[Int]
HK[Option].get(myTT) == typeTag[HK[Option, Int]] //true