1
votes

I have the following code:

import shapeless._
trait TypeLike

case class Arg(name:String)

case class Predicate[Args <: HList, O <: TypeLike](name: String,args:Args,output:O,func: Args => O)(implicit val lubWitness: LUBConstraint[Args, Arg])

(It was result of a question from: Here) And I want to have:

import scalax.collection.edge.LDiEdge
import scalax.collection.immutable.Graph
import scalax.collection.edge.Implicits._
import shapeless._

case class SimplePlan[A<:HList,B<:TypeLike,N<:Predicate[A,B]](plan:Graph[N,LDiEdge])

I get type mismatch and inferred type argument error on following code:

object testMe extends App{
  val p1 = Predicate("Example1", Arg("test")::Arg("test")::HNil, new TypeLike {},
    (args: Arg ::Arg:: HNil) => { println(args(0));new TypeLike {} })


  val p2 = Predicate("Example2", Arg("test")::HNil, new TypeLike {},
    (args: Arg :: HNil) => { println(args(0));new TypeLike {} })

  val x = Graph((p1 ~+> p2)("booo"))
  val p = SimplePlan(x)
}

What exactly I have to add to solve the problem? In case you want to have the dependencies:

scalaVersion := "2.10.4"

libraryDependencies += "com.assembla.scala-incubator" %% "graph-core" % "1.9.1"

libraryDependencies += "com.chuusai" % "shapeless_2.10.4" % "2.1.0"

Edit: (more explanation)

Predicate class is a class with a function "func" that can get variable number of arguments. I implemented this using the question I asked earlier (the link is there) I can instantiate from the Predicate now, p1 and p2. I have another class "SimplePlan" which has a member of a graph that I want its nodes to be a Predicate. But because I'm using HList in Predicate definition, type of each p1 and p2 are different. I get type mismatch error on:

val p = SimplePlan(x)

I really don't know, how should I define SimplePlan to resolve this problem. The exact error is:

Error: inferred type arguments [Nothing,Nothing,Planner.Predicate[_ >: shapeless.::[Planner.Arg,shapeless.HNil] with shapeless.::[Planner.Arg,shapeless.::[Planner.Arg,shapeless.HNil]] <: shapeless.::[Planner.Arg,shapeless.HList], Planner.TypeLike]] do not conform to method apply's type parameter bounds [A <: shapeless.HList,B <: Planner.TypeLike,N <: Planner.Predicate[A,B]]
  val p = SimplePlan(x)
          ^

Error: type mismatch;
 found   : scalax.collection.immutable.Graph[Planner.Predicate[_ >: shapeless.::[Planner.Arg,shapeless.HNil] with shapeless.::[Planner.Arg,shapeless.::[Planner.Arg,shapeless.HNil]] <: shapeless.::[Planner.Arg,shapeless.HList], Planner.TypeLike],scalax.collection.edge.LDiEdge]
 required: scalax.collection.immutable.Graph[N,scalax.collection.edge.LDiEdge]
  val p = SimplePlan(x)
                     ^

Edit 2 (Explanation of what exactly I want): I need this for my research project, so what I want to do could be a little bit weird. I want to have a predicate class that has a function in it with different number and type parameters. I want to have a graph (which is my execution graph). I can get rid of HList for parameters but I will loose type information!

1
It's really hard to tell what you're trying to do here. Can you minimize this and provide the actual error message.Travis Brown
@TravisBrown I added some more details and errors. Sorry, I really didn't know which part should I delete to make it more clear, but I hope the addition part could clarify it.Omid
It looks from here and the error message that you're getting the LUB of Arg :: HNil and Arg :: Arg :: HNil, which isn't anything meaningful. What A do you expect to be inferred for SimplePlan?Travis Brown
@TravisBrown Are Arg:: HNiland 'Arg::Arg::HNil` both <: HList ? I want to have a type for SimplePlan that allows any kind of Predicate? I even don't know if it's possible when I have an HList in my class membersOmid
Yeah, they're both HLists, but there's pretty much nothing you can do with a plain HList.Travis Brown

1 Answers

1
votes

You are having a common problem when dealing with shapeless: Each HList has a different type, and it's very tricky to not lose those types in code that wasn't thinking of HLists in its conception.

You build two predicates, one that is Predicate[Arg::Arg::Nil,TypeLike], and another that is Predicate[Arg::Nil,TypeLike]

The definition of Graph looks like this:

trait Graph[N, E[X] <: EdgeLikeIn[X]]

Notice that Graph does not have any variance: It's asking for one specific type, and you have two incompatible types, so it cannot compile.

If you wonder why they are incompatible, think about this: One of your parameters is a function, taking a specific HList shape, and returning a TypeLike. It does NOT take any HList. The same feature of HList that makes sure you have to match its shape when calling the function is the same that makes it impossible to talk about Predicates generically, like in a regular collection, which is what Graph is actually doing.

You can test that this is the case by changing your code a little, and making a couple of types explicit:

val x = Graph((p1 ~+> p1)("booo")) 
val p = SimplePlan[Arg::Arg::HNil,
          TypeLike,Predicate[Arg::Arg::HNil,TypeLike]](x)

It fails if I do not specify the types: Scala type inference has its limitations. It's also not what you want to do either, because you do not want all your predicates to have the same shape.

We can make the type inference work by being a bit more specific in the definition of SimplePlan. Since you only have one Predicate type, the N <:Predicate[A,B] is not really accomplishing anything for you, so instead we can write

case class SimplePlan[A<:HList, B<:TypeLike](plan:Graph[Predicate[A,B],LDiEdge])

Which works just the same for your sample code. Then

val x = Graph((p1 ~+> p1)("booo"))
val p = SimplePlan(x)

compiles outright, and provides a more descriptive error when you make the edge go from p1 to p2.

The way to solve your problem really depends on what you are trying to do with this data structure. If what you are trying is to recover the exact types without reflection after you put a Predicate into the Graph, I am sad to say that I do not see how that is even possible, as Graph will not keep your specific types: Graph is a container, and is not any more capable of keeping types as a regular List would.

You could wrap predicate in a way that erases the HList type, and then put that into the graph, but the result is still erasing the types before they get into the SimplePlan.

Maybe if you provide more details on what you attempt to do, we can help you better?