Can simplify composite generics? How to avoid [T1 with T2]? - scala

Consider
trait E[C] {
type CONTEXT = C
def doIt(c:CONTEXT): Unit = {}
}
// My doIt method takes a C
case class ESimple[C]() extends E[C]
// ECompound's doIt method must satisfy the requirements of both
// a's and b's doIt parameters.
case class ECompound[C1,C2](a:E[C1],b:E[C2]) extends E[C1 with C2] {
override def doIt(c: CONTEXT): Unit = {
a.doIt(c)
b.doIt(c)
}
}
... and so:
case class Context
val context = Context()
val a:E[Context] = ESimple[Context]()
val b:E[Context] = ESimple[Context]()
val c:E[Context] = ECompound(a,b) // type mismatch
This fails because
val c:E[Context with Context] = ECompound(a,b) // type not mismatched
is the reported type of ECompound(a,b). So, it is possible to resolve this? ECompound(a,b).doIt really should only require a "Context", it is not simplified from "Context with Context"
I realize I can solve this by changing "... extends E[C1 with C2]" to extends[C1], but at the cost of requiring C1==C2, which I'd like to avoid.

One possible way is to define ECompound as:
case class ECompound[C1,C2, CC <: C1 with C2](a:E[C1],b:E[C2]) extends E[CC]
The drawback is that if you don't specify CC explicitly it will be inferred as Nothing, i.e.:
val c: E[Context] =
ECompound(ESimple[Context](), ESimple[Context]()) // works
val c1: E[Context with Context1] =
ECompound(ESimple[Context](), ESimple[Context1]()) // works
val c2 =
ECompound(ESimple[Context](), ESimple[Context1]())
// works, but inferred type is ECompound[Context,Context1,Nothing]

You might consider defining ECompound as:
case class ECompound[CC, C1 >: CC, C2 >: CC](a:E[C1],b:E[C2]) extends E[CC] {
override def doIt(c: CONTEXT): Unit = {
a.doIt(c)
b.doIt(c)
}
}

Well... this is embarrassing.
Turns out that I was compiling to scala 2.10. Using 2.11.8, my original worked out fine, with one small change to E:
trait E[-C] { // Added -C
def doIt(c: C): Unit = {}
def asE: E[C] = this // see below
}
case class ESimple[C]() extends E[C]
//case class ECompound[C1, C2, CC <: C1 with C2](a: E[C1], b: E[C2]) extends E[CC] {
//case class ECompound[CC, C1 >: CC, C2 >: CC](a:E[C1],b:E[C2]) extends E[CC] {
case class ECompound[C1, C2](a: E[C1], b: E[C2]) extends E[C1 with C2] {
override def doIt(c: C1 with C2): Unit = {
a.doIt(c)
b.doIt(c)
}
}
trait Context1
trait Context2
val e1: E[Context1] = ESimple[Context1]()
val e2: E[Context2] = ESimple[Context2]()
val cc11: E[Context1] = ECompound(e1, e1)
val cc12: E[Context1 with Context2] = ECompound(e1, e2)
case class X()
cc12.doIt(new X with Context1 with Context2)
However, the ugly part about this is:
def compileTimeType[T](x: T)(implicit tag: TypeTag[T]) = tag.tpe
val a = ECompound(e1, e1)
val b = ECompound(a, a)
val c = ECompound(b, b)
println( compileTimeType( c.asE ) )
gives:
com.example.E[com.example.Context1
with com.example.Context1
with com.example.Context1
with com.example.Context1
with com.example.Context1
with com.example.Context1
with com.example.Context1
with com.example.Context1]
which (without loss in generality), can be simplified to:
com.example.E[com.example.Context1]
but is not. Sure makes for some nasty compiler errors...
If you can avoid this mess, this answer is yours.

Related

Abstract type and path dependent type in scala

I'd like to use abstract type and type refinement to encode something like a functional dependency between two types.
trait BaseA {
type M
type P <: BaseB.Aux[M]
def f(m: M): Unit
}
trait BaseB {
type M
def m(): M
}
object BaseB {
type Aux[M0] = BaseB { type M = M0 }
}
It means that a A only works with a B if they have the same type M inside.
With the following concret classes
class A extends BaseA {
type M = Int
type P = B
def f(m: Int): Unit = {
println("a")
}
}
class B extends BaseB {
type M = Int
def m(): M = 1
}
So now they have both Int type as M, but the following code does not compile
val a: BaseA = new A
val b: BaseB = new B
def f[T <: BaseA](a: T, b: T#P): Unit = {
a.f(b.m())
}
The compiler tells me that a.f here expect a path dependent type a.M but it got a b.M.
My question here is how can I express the fact that I only need the M type matched in the type level, not the instance level? Or how can I prevent the M in def f(m: M): Unit becoming a path-dependent type?
Thanks!
I think the issue comes from b being related to a type T, and it being possible for a to be a subclass of T that could override M to be something else, making the two objects incompatible. For instance:
class A2 extends BaseA {
type M = String
type P = B2
def f(s: String): Unit = {
println(s)
}
}
class B2 extends BaseB {
type M = String
def m(): M = "foo"
}
val a: BaseA = new A
val b: BaseB = new B2
f[BaseA](a, b)
It seems like if your f were to compile, then all of this should compile too.
You can either make b's type dependent on a.P:
def f(a: BaseA)(b: a.P): Unit
Or I think the whole thing is simplified by not having the compatible types restriction on your classes, but rather require that one is a subclass of the other at the point that they interact:
trait BaseA {
type M
def f(m: M): Unit
}
trait BaseB {
type M
def m(): M
}
class A extends BaseA {
type M = Int
def f(m: Int): Unit = {
println("a")
}
}
class B extends BaseB {
type M = Int
def m(): M = 1
}
val a: A = new A
val b: B = new B
def f(a: BaseA, b: BaseB)(implicit sub: b.M <:< a.M): Unit = {
a.f(sub(b.m()))
}
f(a, b)
Or lastly, consider whether you need these to be path-dependent types at all; could they be regular generic type parameters?
trait BaseA[-M] {
def f(m: M): Unit
}
trait BaseB[+M] {
def m(): M
}
class A extends BaseA[Int] {
def f(m: Int): Unit = {
println("a")
}
}
class B extends BaseB[Int] {
def m(): Int = 1
}
def f[T](a: BaseA[T], b: BaseB[T]): Unit = {
a.f(b.m())
}

Generic derivation of type class instance for ADT

Suppose I've got an ADT and type class Foo like this:
sealed trait A
case class A1() extends A
case class A2() extends A
case class A3() extends A
trait Foo[X] { def foo(x: X): String; }
object Foo {
implicit val a1foo = new Foo[A1] { def foo(a1: A1) = "A1" }
implicit val a2foo = new Foo[A2] { def foo(a2: A2) = "A2" }
implicit val a3foo = new Foo[A3] { def foo(a3: A3) = "A3" }
}
Now I can write Foo[A] like that:
implicit val afoo = new Foo[A] {
def foo(a: A) = a match {
case a1 : A1 => a1foo.foo(a1)
case a2 : A2 => a2foo.foo(a2)
case a3 : A3 => a3foo.foo(a3)
}
}
Unfortunately this code is too boilerplaty. Is it possible to get rid of all that boilerplate and derive Foo[A] automatically (perhaps with shapeless) ?
This afoo implicit is not only useless in the presence of the other three, but even bad, because it will fail with a MatchError on the new A {} value.
I don't see why you would need such instance of Foo[A] when you have implicits that cover all possible (valid) values of type A and afoo doesn't add anything.
I can imagine that if you have a function
def foo(a: A)(implicit f: Foo[A]): String = f.foo(a)
Then, of course, neither of a1foo, a2foo or a3foo will fit. afoo will, so foo(A1()) will compile and work fine, but foo(new A {}) will also compile, and fail with a MatchError. By the way, if the call foo(new A {}) is present in code, it will compile with a warning about non-exhaustive match, but if it's not, it will compile silently.
So the solution to this is to modify foo, to take a more precise type:
def foo[X <: A](a: X)(implicit f: Foo[X]): String = f.foo(a)
Now foo(A1()) will compile and pick a1foo (same with A2 and A3), while foo(new A {}) just won't compile (because it shouldn't).
Update
If you still want to have an instance of Foo[A] as a default case, you can change your code like this:
object Foo extends Foo_1 {
implicit val a1foo: Foo[A1] = ...
implicit val a2foo: Foo[A2] = ...
implicit val a3foo: Foo[A3] = ...
}
trait Foo_1 {
// this implicit has a lower priority:
implicit val afoo: Foo[A] = new Foo[A] { def foo(a: A) = "A" }
}
Now foo(new A {}) or foo(A2(): A) will compile and both return "A".
P.S. by the way, it's recommended to write explicit type of implicits.

Scala - handling "multiple overloaded alternatives of method ... define default arguments"

Let's say I have a setting such as this:
sealed trait Annotation {
def notes : Seq[String]
}
trait Something extends Annotation{
//do something funny
}
case class A(val i:Int)(val notes:Seq[String] = Nil) extends Something
object A{
def apply(a:A)(notes:Seq[String] = Nil):A = A(a.i)(notes)
}
case class B(val b:Boolean)(val notes:Seq[String] = Nil) extends Something
object B{
def apply(b:B)(notes:Seq[String] = Nil):B = B(b.b)(notes)
}
case class C(val s:String)(val notes:Seq[String] = Nil) extends Something
object C{
def apply(c:C)(notes:Seq[String] = Nil) :C = C(c.s)(notes)
}
Trying to compile this will result in
Main.scala:10: error: in object A, multiple overloaded alternatives of method apply define
default arguments.
object A{
^
Main.scala:15: error: in object B, multiple overloaded alternatives of method apply define
default arguments.
object B{
^
Main.scala:20: error: in object C, multiple overloaded alternatives of method apply define
default arguments.
object C{
^
three errors found
I have read this, so I do at least have an idea as to why this is happening, what I don't know, however, is how I am supposed to resolve the issue.
One possibility would - of course - be to simply omit the default values and force the client to provide Nil when no notes are to be stored, but is there a better solution?
My first guess was to simply make the default arguments explicit:
case class A(i: Int)(val notes: Seq[String]) extends Something
object A {
def apply(i: Int): A = new A(i)(Nil)
def apply(a: A)(notes: Seq[String]): A = new A(a.i)(notes)
def apply(a: A): A = new A(a.i)(Nil)
}
However, now, because of currying, you just have a function Int => A and Int => Seq[String] => A (and analogous for A => A) with the same name in scope.
If you refrain from currying you can manually define the overloaded methods:
case class B(b: Boolean, notes: Seq[String]) extends Something
object B {
def apply(b: Boolean): B = B(b, Nil)
def apply(b: B, notes: Seq[String] = Nil): B = B(b.b, notes)
}
But, since notes is now part of the same parameter list as b, the behavior of the case-class methods such as toString is changed.
println(B(true)) // B(true,List())
println(B(true, List("hello"))) // B(true,List(hello))
println(B(B(false))) // B(false,List())
Finally, to mimic the original behavior more closely, you can implement your own equals, hashCode, toString, and unapply methods:
class C(val s:String, val notes:Seq[String] = Nil) extends Something {
override def toString = s"C($s)"
override def equals(o: Any) = o match {
case C(`s`) => true
case _ => false
}
override def hashCode = s.hashCode
}
object C{
def apply(s: String, notes: Seq[String]) = new C(s, notes)
def apply(s: String): C = C(s, Nil)
def apply(c:C, notes:Seq[String] = Nil): C = C(c.s, notes)
def unapply(c: C): Option[String] = Some(c.s)
}
Example:
val c1 = C("hello")
val c2 = C("hello", List("world"))
println(c1) // C(hello)
println(c2) // C(hello)
println(c1 == c2) // true
c1 match { // hello
case C(n) => println(n)
case _ =>
}

Construct a TypeTag from a Class? [duplicate]

I have createOld method that I need to override and I cannot change it. I would like to use TypeTag to pattern match provided type in createNew. The goal is to find out how to call createNew from createOld. My current understanding is that compiler doesn't have enough type information about A in createOld method if it doesn't already come with TypeTag[A].
object TypeTagFromClass {
class C1
class C2
// How to get TypeTag[A] needed by createNew?
def createOld[A](c: Class[A]): A = createNew ???
def createNew[A : TypeTag]: A = {
val result = typeOf[A] match {
case a if a =:= typeOf[C1] => new C1()
case a if a =:= typeOf[C2] => new C2()
}
result.asInstanceOf[A]
}
}
It is possible to create a TypeTag from a Class using Scala reflection, though I'm not sure if this implementation of TypeCreator is absolutely correct:
import scala.reflect.runtime.universe._
def createOld[A](c: Class[A]): A = createNew {
val mirror = runtimeMirror(c.getClassLoader) // obtain runtime mirror
val sym = mirror.staticClass(c.getName) // obtain class symbol for `c`
val tpe = sym.selfType // obtain type object for `c`
// create a type tag which contains above type object
TypeTag(mirror, new TypeCreator {
def apply[U <: Universe with Singleton](m: api.Mirror[U]) =
if (m eq mirror) tpe.asInstanceOf[U # Type]
else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.")
})
}
However, you don't really need full TypeTag if you don't need to inspect generic parameters and full Scala type information. You can use ClassTags for that:
def createNew[A: ClassTag]: A = {
val result = classTag[A].runtimeClass match {
case a if a.isAssignableFrom(classOf[C1]) => new C1()
case a if a.isAssignableFrom(classOf[C2]) => new C2()
}
result.asInstanceOf[A]
}
Or with some implicit sugar:
implicit class ClassTagOps[T](val classTag: ClassTag[T]) extends AnyVal {
def <<:(other: ClassTag[_]) = classTag.runtimeClass.isAssignableFrom(other.runtimeClass)
}
def createNew[A: ClassTag]: A = {
val result = classTag[A] match {
case a if a <<: classTag[C1] => new C1()
case a if a <<: classTag[C2] => new C2()
}
result.asInstanceOf[A]
}
You can simplify that even further by using plain old Java newInstance() method:
def createNew[A: ClassTag]: A = classTag[A].runtimeClass.newInstance().asInstanceOf[A]
This, of course, would only work if you don't need different constructor parameters for different classes.
Calling this createNew from createOld is much simpler than the one with TypeTags:
def createOld[A](c: Class[A]): A = createNew(ClassTag[A](c))
So it is not very safe and correct (cause you don't use the power of scala type system), but you can make (using your logic) to do the following:
def createNew[A](implicit t: TypeTag[A]): A = {
val result: Any = t.tpe.toString match {
case "C1" => new C1
case "C2" => new C2
}
result.asInstanceOf[A]
}
createNew[C1] //> its all ok
createNew[C2] //> its all ok
createNew[C3] //> crashes here; lets pretend we got C3 class
To use it with createOld, just pass implicit argument:
def createOld[A](c: Class[A])(implicit t: TypeTag[A]): A = createNew[A]
createOld[C1] //> its all ok
createOld[C2] //> its all ok
createOld[C3] //> crashes here; lets pretend we got C3 class
I think I should not tell you twice that it is not very good.
We can improve this code by using shapeless:
Lets create a poly function, which has a TypeTag as an argument:
import shapeless._; import scala.reflect.runtime.universe._;
def getTypeTag[T](implicit t: TypeTag[T]) = t //> to get TypeTag of a class
// here is low prority implicit
trait createPolyNewErr extends Poly1 {
implicit def newErr[T] = at[T](_ => "Error can not create object of this class")
}
object createPolyBew extends createPolyNewError {
implicit def newC1 = at[TypeTag[C1]](_ => new C1)
implicit def newC2 = at[TypeTag[C2]](_ => new C2)
}
createPolyNew(getTypeTag[C1]) //> success
createPolyNew(getTypeTag[C2]) //> success
createPolyNew(getTypeTag[C3]) //> String: Error can not create object of this class no crash!
We also can write a function, in order not to use function getTypeTag[T] every time:
def createPoly[T]
(implicit t: TypeTag[T],
cse: poly.Case[createPolyNew.type, TypeTag[T] :: HNil]) = cse(t)
createPoly[C1] //> its all ok
createPoly[C2] //> its all ok
createPoly[C3] //> String: Error can not create object of this class no crash!

Get TypeTag[A] from Class[A]

I have createOld method that I need to override and I cannot change it. I would like to use TypeTag to pattern match provided type in createNew. The goal is to find out how to call createNew from createOld. My current understanding is that compiler doesn't have enough type information about A in createOld method if it doesn't already come with TypeTag[A].
object TypeTagFromClass {
class C1
class C2
// How to get TypeTag[A] needed by createNew?
def createOld[A](c: Class[A]): A = createNew ???
def createNew[A : TypeTag]: A = {
val result = typeOf[A] match {
case a if a =:= typeOf[C1] => new C1()
case a if a =:= typeOf[C2] => new C2()
}
result.asInstanceOf[A]
}
}
It is possible to create a TypeTag from a Class using Scala reflection, though I'm not sure if this implementation of TypeCreator is absolutely correct:
import scala.reflect.runtime.universe._
def createOld[A](c: Class[A]): A = createNew {
val mirror = runtimeMirror(c.getClassLoader) // obtain runtime mirror
val sym = mirror.staticClass(c.getName) // obtain class symbol for `c`
val tpe = sym.selfType // obtain type object for `c`
// create a type tag which contains above type object
TypeTag(mirror, new TypeCreator {
def apply[U <: Universe with Singleton](m: api.Mirror[U]) =
if (m eq mirror) tpe.asInstanceOf[U # Type]
else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.")
})
}
However, you don't really need full TypeTag if you don't need to inspect generic parameters and full Scala type information. You can use ClassTags for that:
def createNew[A: ClassTag]: A = {
val result = classTag[A].runtimeClass match {
case a if a.isAssignableFrom(classOf[C1]) => new C1()
case a if a.isAssignableFrom(classOf[C2]) => new C2()
}
result.asInstanceOf[A]
}
Or with some implicit sugar:
implicit class ClassTagOps[T](val classTag: ClassTag[T]) extends AnyVal {
def <<:(other: ClassTag[_]) = classTag.runtimeClass.isAssignableFrom(other.runtimeClass)
}
def createNew[A: ClassTag]: A = {
val result = classTag[A] match {
case a if a <<: classTag[C1] => new C1()
case a if a <<: classTag[C2] => new C2()
}
result.asInstanceOf[A]
}
You can simplify that even further by using plain old Java newInstance() method:
def createNew[A: ClassTag]: A = classTag[A].runtimeClass.newInstance().asInstanceOf[A]
This, of course, would only work if you don't need different constructor parameters for different classes.
Calling this createNew from createOld is much simpler than the one with TypeTags:
def createOld[A](c: Class[A]): A = createNew(ClassTag[A](c))
So it is not very safe and correct (cause you don't use the power of scala type system), but you can make (using your logic) to do the following:
def createNew[A](implicit t: TypeTag[A]): A = {
val result: Any = t.tpe.toString match {
case "C1" => new C1
case "C2" => new C2
}
result.asInstanceOf[A]
}
createNew[C1] //> its all ok
createNew[C2] //> its all ok
createNew[C3] //> crashes here; lets pretend we got C3 class
To use it with createOld, just pass implicit argument:
def createOld[A](c: Class[A])(implicit t: TypeTag[A]): A = createNew[A]
createOld[C1] //> its all ok
createOld[C2] //> its all ok
createOld[C3] //> crashes here; lets pretend we got C3 class
I think I should not tell you twice that it is not very good.
We can improve this code by using shapeless:
Lets create a poly function, which has a TypeTag as an argument:
import shapeless._; import scala.reflect.runtime.universe._;
def getTypeTag[T](implicit t: TypeTag[T]) = t //> to get TypeTag of a class
// here is low prority implicit
trait createPolyNewErr extends Poly1 {
implicit def newErr[T] = at[T](_ => "Error can not create object of this class")
}
object createPolyBew extends createPolyNewError {
implicit def newC1 = at[TypeTag[C1]](_ => new C1)
implicit def newC2 = at[TypeTag[C2]](_ => new C2)
}
createPolyNew(getTypeTag[C1]) //> success
createPolyNew(getTypeTag[C2]) //> success
createPolyNew(getTypeTag[C3]) //> String: Error can not create object of this class no crash!
We also can write a function, in order not to use function getTypeTag[T] every time:
def createPoly[T]
(implicit t: TypeTag[T],
cse: poly.Case[createPolyNew.type, TypeTag[T] :: HNil]) = cse(t)
createPoly[C1] //> its all ok
createPoly[C2] //> its all ok
createPoly[C3] //> String: Error can not create object of this class no crash!