How to match on type member in function signature in Scala? - scala

For obscure reasons due to a specific library I'm using involving macros and serialization, I have chosen to try to use type members instead of type parameters for a class (background: Is there a way to use ClassTags with the Autowire library in Scala?)
sealed trait Command {
type M <: CommandMetaData
val meta: M
}
final case class SysCmdMetaData(
// ...
) extends CommandMetaData
// How to guarantee cmd.M <: SysCmdMetaData
def runInSystem(cmd: Command){
//...
}
Is there a way to do this statically? I think using type classes and context bounds is one option, but I feel it is a bit heavy handed since I'm already using subclassing here. Perhaps the better option is just to make metadata a trait, and embrace object oriented more fully and create all kinds of variations of Command based on which traits are mixed in.

You can define the function argument with a refined type:
def runInSystem(cmd: Command { type M <: SysCmdMetaData }) = { }
Note that unlike type parameters, type members are not inferred by default. The following code will fail:
scala> val cmd: Command = new Command {
type M = SysCmdMetaData
val meta = SysCmdMetaData()
}
cmd: Command = $anon$1#830b8a5
scala> runInSystem(cmd)
<console>:15: error: type mismatch;
found : cmd.type (with underlying type Command)
required: Command{type M <: SysCmdMetaData}
runInSystem(cmd)
^
You'd have to either:
Use some named subclass of Command, where M is statically fixed to an appropriate type.
Pass an anonymous subclass instance directly to runInSystem:
runInSystem(new Command {
type M = SysCmdMetaData
val meta = SysCmdMetaData()
})
Manually prescribe the type member of the variable you pass to runInSystem:
val cmd: Command { type M = SysCmdMetaData } = new Command {
type M = SysCmdMetaData
val meta = SysCmdMetaData()
}
runInSystem(cmd)
You can also define a type alias with a type parameter:
object Command {
type Aux[M0 <: CommandMetaData] = Command { type M = M0 }
}
val cmd: Command.Aux[SysCmdMetaData] = ???
runInSystem(cmd)

Related

Scala: value class X is added to the return type of its methods as X#

I'd like to enrich a 'graph for scala' graph. For this purpose i've created an implicit value class:
import scalax.collection.mutable
import scalax.collection.edge.DiEdge
...
type Graph = mutable.Graph[Int, DiEdge]
implicit class EnrichGraph(val G: Graph) extends AnyVal {
def roots = G.nodes.filter(!_.hasPredecessors)
...
}
...
The problem lies with the return type of its methods, e.g.:
import ....EnrichGraph
val H: Graph = mutable.Graph[Int,DiEdge]()
val roots1 = H.nodes.filter(!_.hasPredecessors) // type Iterable[H.NodeT]
val roots2 = H.roots // type Iterable[RichGraph#G.NodeT] !!
val subgraph1 = H.filter(H.having(roots1)) // works!
val subgraph2 = H.filter(H.having(roots2)) // type mismatch!
Does the cause lie with fact that 'Graph' has dependent subtypes, e.g. NodeT? Is there a way to make this enrichment work?
What usually works is propagating the singleton type as a type parameter to EnrichGraph. That means a little bit of extra boilerplate since you have to split the implicit class into a class and an implicit def.
class EnrichGraph[G <: Graph](val G: G) extends AnyVal {
def roots: Iterable[G#NodeT] = G.nodes.filter(!_.hasPredecessors)
//...
}
implicit def EnrichGraph(g: Graph): EnrichGraph[g.type] = new EnrichGraph[g.type](g)
The gist here being that G#NodeT =:= H.NodeT if G =:= H.type, or in other words (H.type)#NodeT =:= H.NodeT. (=:= is the type equality operator)
The reason you got that weird type, is that roots has a path type dependent type. And that path contains the value G. So then the type of val roots2 in your program would need to contain a path to G. But since G is bound to an instance of EnrichGraph which is not referenced by any variable, the compiler cannot construct such a path. The "best" thing the compiler can do is construct a type with that part of the path left out: Set[_1.G.NodeT] forSome { val _1: EnrichGraph }. This is the type I actually got with your code; I assume you're using Intellij which is printing this type differently.
As pointed out by #DmytroMitin a version which might work better for you is:
import scala.collection.mutable.Set
class EnrichGraph[G <: Graph](val G: G) extends AnyVal {
def roots: Set[G.NodeT] = G.nodes.filter(!_.hasPredecessors)
//...
}
implicit def EnrichGraph(g: Graph): EnrichGraph[g.type] = new EnrichGraph[g.type](g)
Since the rest of your code actually requires a Set instead of an Iterable.
The reason why this still works despite reintroducing the path dependent type is quite tricky. Actually now roots2 will receive the type Set[_1.G.NodeT] forSome { val _1: EnrichGraph[H.type] } which looks pretty complex. But the important part is that this type still contains the knowledge that the G in _1.G.NodeT has type H.type because that information is stored in val _1: EnrichGraph[H.type].
With Set you can't use G#NodeT to give you the simpler type signatures, because G.NodeT is a subtype of G#NodeT and Set is unfortunately invariant. In our usage those type will actually always be equivalent (as I explained above), but the compiler cannot know that.

Scala Type Mismatch issue when using import from different scope

In my project(play 2.4 with slick 3.0), I have a setup: using MySQL for production and H2 for unit testing(start to feel that it is a horrible idea). To do that, I have to import slick.driver.JdbcProfile instead of any specific driver (e.g. slick.driver.MySQLDriver) and import its api._ in my DAO class, like this:
class SomeDAO(val context: MyContext){
import context.profile.api._
import context.SOME_TYPE
def invoke(para: SOME_TYPE) = { ... }
// other DBIO...
}
Everything is OK so far, however, I have a service class that requires this DAO, and a type in context as a parameter:
class MyService(val context: MyContext){
val someDAO = new SomeDAO(context)
import someDAO.context.SOME_TYPE // it works
// import context.SOME_TYPE // Type mismatch! But how to avoid it???
def invokeDAOMethod(para: SOME_TYPE) = someDAO.invoke(para) // where mismatch occurred
// expect: MyService.this.someDao.context.SOME_TYPE
// actual: MyService.this.context.SOME_TYPE
}
Problem heppens when I try to import a type from exact the same context instance, intuitively speaking, I'm using the "Same" type here, right?
Can someone explain this kind of behaviour and give hint about patterns to cut this kind of 'non-sense' I wrote?
You are using path-dependent types. They are literally dependent on paths, not on its content.
Consider that example:
class Store {
case class Box[T](box : T)
def box[T](b : T) = Box[T](b)
def unbox[T](b : Box[T]) : T = b.box
}
object Assign {
val x = new Store()
val y = x
val box = x.box[Int](2)
val ub = y.unbox[Int](box)
}
You may assume naively that x and y are practically identical, they share the same content and the same type. True for the content. Wrong for types. And compiler nicely provide you with a type error:
error: type mismatch;
found : dependent.Assign.x.Box[Int]
required: dependent.Assign.y.Box[Int]
But you could tell the compiler that x and y should share the same path-dependent types despite being different literal paths
object Assign {
val x = new Store()
val y : x.type = x
val box = x.box[Int](2)
val ub = y.unbox[Int](box)
}
With val y : x.type = x the compiler would success.
Your issue has the similar nature, and the solution is similar too: you should specify type equivalence explicitly. Change the SameDAO definition to accept type parameter
class SomeDAO[MC <: MyContext](val context: MC){
...
}
and pass appropriate type upon creation:
class MyService(val context: MyContext){
val someDAO = new SomeDAO[context.type](context)
...
}
Reduce the pain by moving the type definition to a object, therefore the type is imported statically.
Still not sure how to inherit types and how to introduce some dynamic components into this object
UPDATE: it is actually called "Path Dependent Types" in Scala.

Composing type-level functions with implicit witnesses

I am experimenting with some rather complex type-level calculations. There, I have some type tags (say, A, B, and C), and functions working on them, which are represented by implicit witnesses with path-dependent result types:
class A
class B
class C
trait F1[T] { type result }
trait F2[T] { type result }
implicit object f1OfA extends F1[A] { type result = B }
implicit object f2OfB extends F2[B] { type result = C }
trait Composed[T] { type result }
In the course of calculations, when "implementing" Composed, I need to make use of the fact that I can, given the above code, in principle transform A to C (in this example, I just need the composition, but actually there's more things involved).
However, I don't know how to express the composition, as I am always limited by the restriction that implicits are not transitively applied; the following code fails with "implicit not found":
implicit def composed1[X](implicit f2DotF1OfX: F2[F1[X]]): Composed[X] =
new Composed[X] { type result = f2DotF1OfX.result }
implicitly[Composed[C]]
What I actually tried to write initially was the following:
implicit def composed2[X](implicit f1OfX: F1[X], f2OfLast: F2[f1OfX.result]): Composed[X] =
new Composed[X] { type result = f2OfLast.result }
This of course failed because there I used f1OfLast in the same parameter list it is defined. If it were not an implicit parameter, I could write
implicit def composed3a[X](f1OfX: F1[X])(f2OfLast: F2[f1OfX.result]): Composed[X] =
new Composed[X] { type result = f2OfLast.result }
But it is not possible to do this using two implicit parameter lists, since they are disallowed by the language.
In short: how can I get a witness for F2[F1[X]] in the above examples? If necessary, I could also change the way of writing type-level functions, but I have not yet found another way to express them.
You use another type parameter and then require f1OfX.result to be that:
implicit def composed4[X, Y](implicit f1OfX: F1[X] { type result = Y },
f2OfLast: F2[Y]): Composed[X] =
new Composed[X] { type result = f2OfLast.result }

how to generically handle Scala Higher-kinded types when coding factories for generating containers of items of type 'X'

After working through some examples of Scala Higher-kinded types in this tutorial, I started wondering if it is possible to write a method that generically handles two subclasses of a trait that
is defined as a higher-kinded type.
The tutorial defines (a slightly more complex version of) this trait:
trait ContainerFactory[M[_]] { def put[A](x: A): M[A] }
Which I understand as the signature of a type parameterized factory that creates different kinds of containers (Lists, Sets, etc., where the type of container is given by M) and where the type of the object inserted into the container via the put method is given by A. At the invocation site (I think that's the correct term) where you
instantiate the container, you specify the type of container you want (as in the line with the comment: //factory for List containers)
val factory = new ContainerFactory[List] { def put[A](x: A) = List(x) } // factory for List containers
factory.put("dog") // insert an element of type String to factory
res5: List[String] = List(dog) // and you get a List of String
factory.put(1)// insert an element of type Int to factory
res6: List[Int] = List(1) // and you get a List of Int
val factory2 = new ContainerFactory[Set] { def put[A](x: A) = Set(x)} // factory for Set containers
factory2.put("dog")
factory2.put(1)
My goal is to create a method that takes a ContainerFactory
and an object to put into the generated container. I would like that method to generate the appropriate container (List or Set) parameterized to hold the type of object that I pass in as the second object.
I think a method like the one below would be really cool and useful, but I am having trouble with the Scala syntax to get it to work. In fact, I don't even know if it is possible.
// Code below does not compile
// Method for generating container (of type defined by first arg) that contains the second argument, and
// which (after instantiation) is parameterized to hold only objects of that type:
def genContainer[M[T]](factory: ContainerFactory[M], item : T) = {
factory.put(item)
}
genContainer(factory2, "x")
// desired return value => Set[String] = Set(x)
genContainer(factory, 11)
// desired return value => List[Int] = List(11)
Note: the error I get when I try to define genContainer is:
<console>:10: error: not found: type T
def genContainer[M[T]]( factory : Container[M] , item : T) = {
Note 2: I can define a method like this, which takes a generic ContainerFactory
def genContainer[M[T]](factory: ContainerFactory[M]) = { }
But when I try to specify the second argument as type T (which is referenced in the parameterization) I get the error about T not found.
You're very close. The issue is that you need to declare your type parameters separately:
def genContainer[T, M[_]](factory: ContainerFactory[M], item: T) =
factory.put(item)
This is a little confusing because the following compiles:
def genContainer[M[T]](factory: ContainerFactory[M]) = "whatever"
The scope of the T here is limited to the inside of the M[...], though (see section 4.4 of the language specification for details). This can be convenient when you're declaring fancy bounds like M[T <: Foo[T]], but in general giving the type parameter of the type constructor a name is just noise, and it's best to go with M[_] (which is exactly equivalent to M[A]).
You were really close:
def genContainer[T, M[_]](factory: ContainerFactory[M], item: T) = {
factory.put(item)
}
All you have to do is specify each type parameter as a top-level type parameter! And the compiler is smart enough to deduce these type parameters under many circumstances:
val factory = new ContainerFactory[List] { def put[A](x: A) = List(x) }
genContainer(factory, "foo") //No need to specify the type parameters!

Why can't a wildcard type parameter is Scala be bound?

I have a typed pair class:
class TypedPair[T]
and I want to apply a certain function to a heterogeneous sequence of them:
def process[T](entry: TypedPair[T]) = {/* something */}
Why doesn't this work?
def apply(entries: TypedPair[_]*) = entries.foreach(process)
It fails with the error:
error: polymorphic expression cannot be instantiated to expected type;
found : [T](TypedPair[T]) => Unit
required: (TypedPair[_]) => ?
def apply(entries: TypedPair[_]*) = entries.foreach(process)
I don't recall getting into this problem in Java...
The compiler has problems figuring out the anonymous method in this case. When you added the dummy parameter, you also changed the syntax to help the compiler with it, so the following will work:
def apply(entries: TypedPair[_]*) = entries.foreach(process(_))
You have declared an existential type:
def apply(entries: TypedPair[_]*) = entries.foreach(process)
is equivalent to
def apply(entries: TypedPair[t] forSome { type t }*) = entries.foreach(process)
I'm not sure if this is what you intended or not.