I am trying to find an elegant way to do the following.
Let's assume we have the following classes:
case class Foo(fooName: String, bar: Bar, baz: Baz)
case class Bar(barName: String, baz: Baz)
case class Baz(bazName: String)
I want to be able to change the name (in this case of Foo and its children objects in some way), for example, to prefix them with some title.
I might define a typeclass which serves that purpose:
trait Titled[T] {
def titled(t: T)(title: String): T
}
For Foo, the implementation would do this:
implicit val fooTitled: Titled[Foo] = new Titled[Foo] {
def titled(foo: Foo)(title: String): Foo = foo.copy(fooName = title + fooName)
}
There could be other implementations, such as Esquired, which would suffix the name with "Esq."
But this does not update the title of the children values, bar and baz, which I would like.
What I'd eventually like to end up with is something like this:
//import all the implicits needed
val foo = Foo(...)
val titledFoo = foo.title("Mr. ") // names are prefixed with Mr.
val andEsquired = foo.esquire // names are also suffixed with Esq
Now, I've been looking online to see if it is possible to do, and I've seen shapeless library, but I'm still not on that level of Scala knowledge that I could decipher and create such "magic" if its even possible.
I am not necessarily looking for a complete solution (although I would appreciate one), but if someone could point me in the right direction, show me some examples, tutorials or whatever on things like these, I'd be very grateful.
Try the following type class. I assume that all case classes that should have type class instance have the first field of type String to be prefixed.
import shapeless.ops.hlist.IsHCons
import shapeless.{::, Generic, HList, HNil}
trait Titled[T] {
def titled(t: T)(title: String): T
}
object Titled {
//implicit def identity[A]: Titled[A] = new Titled[A] {
// override def titled(t: A)(title: String): A = t
//}
implicit def transform[A <: Product, L <: HList, H, T <: HList](implicit
generic: Generic.Aux[A, L],
isHCons: IsHCons.Aux[L, String, T],
tailTitled: HListTitled[T]): Titled[A] = new Titled[A] {
override def titled(t: A)(title: String): A = {
val l = generic.to(t)
val head = isHCons.head(l)
val tail = isHCons.tail(l)
val newHead = title + head
val newTail = tailTitled.titled(tail)(title)
val newL = isHCons.cons(newHead, newTail)
generic.from(newL)
}
}
}
trait HListTitled[L <: HList] {
def titled(t: L)(title: String): L
}
object HListTitled {
implicit val hnil: HListTitled[HNil] = new HListTitled[HNil] {
override def titled(t: HNil)(title: String): HNil = HNil
}
implicit def hcons[H, T <: HList](implicit
headTitled: Titled[H],
tailTitled: HListTitled[T]): HListTitled[H :: T] = new HListTitled[H :: T] {
override def titled(t: H :: T)(title: String): H :: T =
headTitled.titled(t.head)(title) :: tailTitled.titled(t.tail)(title)
}
}
implicit class TitledOps[T](t: T) {
def titled(title: String)(implicit ttld: Titled[T]): T = ttld.titled(t)(title)
}
case class Foo(fooName: String, bar: Bar, baz: Baz)
case class Bar(barName: String, baz: Baz)
case class Baz(bazName: String)
case class Foo1(fooName: String, bar: Bar, baz: Baz, i: Int)
Foo("Johnson", Bar("Smith", Baz("Doe")), Baz("James")).titled("Mr. ")
// Foo(Mr. Johnson,Bar(Mr. Smith,Baz(Mr. Doe)),Baz(Mr. James))
Foo1("Johnson", Bar("Smith", Baz("Doe")), Baz("James"), 10).titled("Mr. ")
// doesn't compile
// if you want it to compile uncomment "identity"
Related
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.
In the example below I have a type class Foo, and would like to somehow guarantee that all members conforming to Foo (such as Bar via barFoo) have a copy method such as the one generated by way of being a case class. I haven't thought of a way to do this. In this case the copy signature would be possibly be something like copy(foo: F, aa: List[T] = foo.aa, maybe: Option[T] = foo.maybe): F.
trait Foo[F] {
type T
def aa(foo: F): List[T]
def maybe(foo: F): Option[T]
}
final case class Bar(aa: List[String], maybe: Option[String])
object Bar {
implicit val barFoo = new Foo[Bar] {
type T = String
def aa(foo: Bar): List[String] = foo.aa
def maybe(foo: Bar): Option[T] = foo.maybe
}
}
I couldn't do it with type member, but here you are a version with type parameters. Also it is necessary to add a method to Foo to construct the object.
trait Foo[F, T] {
def aa(foo: F): List[T]
def maybe(foo: F): Option[T]
def instance(aa: List[T], maybe: Option[T]): F
}
class Bar(val aa: List[String], val maybe: Option[String]) {
override def toString = s"Bar($aa, $maybe)"
}
object Bar {
implicit val barFoo = new Foo[Bar, String] {
def aa(foo: Bar): List[String] = foo.aa
def maybe(foo: Bar): Option[String] = foo.maybe
def instance(aa: List[String], maybe:Option[String]):Bar = new Bar(aa, maybe)
}
}
implicit class FooOps[A, T](fooable:A)(implicit foo:Foo[A, T]) {
def copy(aa: List[T] = foo.aa(fooable), maybe: Option[T] = foo.maybe(fooable)) = {
foo.instance(aa, maybe)
}
}
val r = new Bar(List(""), Option("")).copy(aa = List("asd"))
println(r)
Let's say I have a function such as:
def foo[T<:U <:V](t:T): Unit
I want to know if there is a way of combining these two in a type W so that I can have:
def foo[T<: W](t: T): Unit
The use case for this is:
trait FooTrait{
type W
def foo[T<: W](t: T): Unit
}
I might have two different implementations of foo, one of them with a simple W1 type bound whereas the other one is T<:W1 <:W2 I want the more complex one to define a W3 so that I can have:
def foo[T<: W3](t: T): Unit
Similiarly, I want to be able to do these with type classes. So if I have:
def bar[T<:U :V](t:T): Unit
I want to have
def bar[T:X](t:T): Unit
The use case for this is essentially the same as the earlier case.
is this possible?
In the first part of your question, the syntax isn't even valid.
If you want to impose multiple upper bounds U, V on some type T,
you have to use with keyword anyway:
trait A
trait B
def f[X <: A with B](x: X): Unit = ???
This here doesn't work:
// def f[X <: A <: B](x: X): Unit = ??? // doesn't compile
To address the second part of your question, I would like to
explain why something like with doesn't work for typeclasses.
This here does work:
trait Foo[X]
trait Bar[X]
def g[X: Foo : Bar](x: X): Unit = ???
This here doesn't work:
// def g[X: Foo with Bar](x: X): Unit = ??? // nope
Why? Because
def foo[X: Y](x: X): Ret = ???
is actually a shortcut for
def foo[X](x: X)(implicit y: Y[X]): Ret = ???
If you would try to somehow amalgamate two different typeclasses Foo and Bar,
this would result in the following desugared code:
def foo[X](x: X)(implicit superPowerfulThing: (Foo somehowGluedWith Bar)[X]): Ret = ???
But this is obviously not something that you should want.
Instead, what you want is:
def foo[X](x: X)(implicit foo: Foo[X], bar: Bar[X])(x: X): Ret = ???
In this way, the two requirements Foo and Bar can be supplied independently,
which wouldn't work if you requested some superPowerfulThing that implements
both Foo and Bar at once.
Hope that clarifies why something works or doesn't work.
Andrey Tyukin has correctly point out that with is probably the answer for your first question and that the second question has no direct solution. However if you are OK with indirect solutions, you might work this around by introducing a new type class that will says that the target types belongs to the two original type classes. Here is a simple example for imaginary typeclasses Foo and Bar and for a base trait Base with a specific implementation ChildFooBar:
trait Foo[X] {
def doFoo(x: X): String
}
trait Bar[X] {
def doBar(x: X): String
}
trait Base {
type W[_]
def baz[T: W](t: T): String
}
class ChildFooBar extends Base {
import ChildFooBar._
type W[X] = FooAndBar[X]
override def baz[T: FooAndBar](t: T): String = {
val foo = implicitly[Foo[T]]
val bar = implicitly[Bar[T]]
foo.doFoo(t) + bar.doBar(t)
}
}
object ChildFooBar {
#implicitNotFound("The type should provide both implicit Foo and implicit Bar.")
case class FooAndBar[X](foo: Foo[X], bar: Bar[X])
object FooAndBar {
implicit def fromFooAndBar[X](implicit foo: Foo[X], bar: Bar[X]): FooAndBar[X] = FooAndBar(foo, bar)
}
// private is important here to avoid diversion of implicits
private implicit def toFoo[X](implicit fooAndBar: FooAndBar[X]): Foo[X] = fooAndBar.foo
private implicit def toBar[X](implicit fooAndBar: FooAndBar[X]): Bar[X] = fooAndBar.bar
}
Now if SomeClass is a member of both Foo and Bar typeclasses, then
case class SomeClass(foo: Int, bar: Double)
object SomeClass {
implicit val foo: Foo[SomeClass] = new Foo[SomeClass] {
override def doFoo(x: SomeClass) = s"foo = ${x.foo}"
}
implicit val bar: Bar[SomeClass] = new Bar[SomeClass] {
override def doBar(x: SomeClass) = s"bar = ${x.bar}"
}
}
the simple code
println(new ChildFooBar().baz(SomeClass(1, 2.0)))
will compile and work as expected.
I am trying to create a type class that will allow me to increment an Int field called "counter" on any case class, as long as that class has such a field.
I have tried to do this with Shapeless but am hitting walls (after first trying to digest "The Type Astronaut's Guide to Shapeless", the "Feature overview" of Shapeless 2.0.0 and numerous threads on Stack Overflow).
What I want is to be able to do something like
case class MyModel(name:String, counter:Int) {}
val instance = MyModel("Joe", 4)
val incremented = instance.increment()
assert(incremented == MyModel("Joe", 5))
And it should work for any case class with a suitable counter field.
I thought that this would be possible using a type class and Shapeless' record abstraction (and an implicit conversion to get the increment functionality added as a method). The bare bones would be something like this:
trait Incrementer[T] {
def inc(t:T): T
}
object Incrementer {
import shapeless._ ; import syntax.singleton._ ; import record._
implicit def getIncrementer[T](implicit generator: LabelledGeneric[T]): Incrementer[T] = new Incrementer[T] {
def inc(t:T) = {
val repr = generator.to(t)
generator.from(repr.replace('counter, repr.get('counter) + 1))
}
}
}
However, this does not compile. The error being value replace is not a member of generator.Repr. I guess this is because the compiler does not have any guarantee as to T having a field called counter and it being of type Int. But how could I tell it so? Is there better/more documentation on Shapeless' record? Or is this a completely wrong way to go?
You have to just implicitly require a Modifier
import shapeless._
import ops.record._
implicit class Incrementer[T, L <: HList](t: T)(
implicit gen: LabelledGeneric.Aux[T, L],
modifier: Modifier.Aux[L, Witness.`'counter`.T, Int, Int, L]
) {
def increment(): T = gen.from(modifier(gen.to(t), _ + 1))
}
You can easily do it with a simple typeclass derivation:
trait Incrementer[T] {
def inc(s: Symbol)(t: T): T
}
object Incrementer {
def apply[T](implicit T: Incrementer[T]): Incrementer[T] = T
implicit def head[Key <: Symbol, Head, Tail <: HList](implicit Key: Witness.Aux[Key], Head: Numeric[Head]) = new Incrementer[FieldType[Key, Head] :: Tail] {
import Head._
override def inc(s: Symbol)(t: FieldType[Key, Head] :: Tail): (FieldType[Key, Head] :: Tail) =
if (s == Key.value) (t.head + fromInt(1)).asInstanceOf[FieldType[Key, Head]] :: t.tail
else t
}
implicit def notHead[H, Tail <: HList](implicit Tail: Incrementer[Tail]) = new Incrementer[H :: Tail] {
override def inc(s: Symbol)(t: H :: Tail): H :: Tail = t.head :: Tail.inc(s)(t.tail)
}
implicit def gen[T, Repr](implicit gen: LabelledGeneric.Aux[T, Repr], Repr: Incrementer[Repr]) = new Incrementer[T] {
override def inc(s: Symbol)(t: T): T = gen.from(Repr.inc(s)(gen.to(t)))
}
}
case class Count(counter: Int)
case class CountAndMore(more: String, counter: Int)
case class FakeCount(counter: Long)
object Test extends App {
println(Incrementer[Count].inc('counter)(Count(0)))
println(Incrementer[CountAndMore].inc('counter)(CountAndMore("", 0)))
println(Incrementer[FakeCount].inc('counter)(FakeCount(0)))
}
I got the following issue when trying to use typeclasses throughout my project.
trait FooAble[T] { def fa(t: T): List[T] }
object Foo { def apply[T](t: T) = implicitly[FooAble[T]].fa(t) }
trait BarAble[T] { def fb(t: T): Double }
object Bar { def apply[T](t: T) = implicitly[BarAble[T]].fb(t) }
And would like to be able to do the following:
// xs contains elements of type A and B which are subclasses of the trait Something
def f(xs: List[Something]) = {
val something = xs.map(Foo)
val somethingElse = xs.map(Bar)
}
However, this would not work as we don't know if Something implements A[]and B[], no implicit implementation found. What do I need to do so that the elements of the list xs implement the typeclasses FooAble and BarAble?
I think this question: What are type classes in Scala useful for? will help you to understand the proper use (& usefulness) of type classes.
Am just extending the answer by Kevin Wright in the above link for your use case (if I understand your need correctly!):
trait Addable[T] {
def zero: T
def append(a: T, b: T): T
}
trait Productable[T] {
def zero: T
def product(a: T, b: T): T
}
implicit object IntIsAddable extends Addable[Int] {
def zero = 0
def append(a: Int, b: Int) = a + b
}
implicit object IntIsProductable extends Productable[Int] {
def zero = 1
def product(a: Int, b: Int) = a*b
}
def sumAndProduct[T](xs: List[T])(implicit addable: Addable[T], productable: Productable[T]) =
(xs.foldLeft(addable.zero)(addable.append), xs.foldLeft(productable.zero)(productable.product))
So akin to above, in your use case, you need to provide implicit objects which implement your type classes FooAble & BarAble and your method signature for function f becomes:
def f[Something](xs: List[Something])(implicit fooable: FooAble[Something], barable: BarAble[Something])