Scala fails to infer the right type arguments

前端 未结 2 1351
情书的邮戳
情书的邮戳 2020-12-16 06:30

Background info: I\'m currently trying to set up a generic graph library that includes a few different search algorithms (I\'ve started with Dijkstra\'s). I

相关标签:
2条回答
  • 2020-12-16 06:42

    Daniel is probably right that the existing Scala type inferencer needs more direct information to figure out the that E must be Edge. Also, it is my understanding that type inference is intentionally under-specified to make way for future improvements.

    Anyway, I think you can take another approach to your design that will resolve the type inference problem: use type members rather than parameters. I've illustrated what I mean with self-contained code below. The key idea is that types E and V become part of the GraphOps type, but they can still be surfaced as type parameters by using a type refinement, as in the Dijkstra method.

    trait GraphOps { type E; type V }
    trait WeightedGraphOps extends GraphOps { }
    trait DirectedGraphOps extends GraphOps { }
    object GraphOps{
      def Dijkstra[V0, G <: (WeightedGraphOps{type V = V0})
                             with (DirectedGraphOps{type V = V0})]
          (graph:G, start:V0) = { }
    }
    
    case class Position(x: Int, y: Int)
    case class Edge()
    
    case class GraphMap[T]() extends WeightedGraphOps with DirectedGraphOps {
      type E = Edge
      type V = Position
    }
    
    object Test {
      val graph = new GraphMap[Int]( )
      GraphOps.Dijkstra(graph, Position(0,0))
    }
    

    Edit: One potential limitation of this type member approach is that puts less constraints on the type parameter G in method Dijkstra. Specifically, the bounds WeightedGraphOps and DirectedGraphOps aren't constrained to have the same type members E. I'm not sure how to resolve this without bumping into the type inference problem you originally reported. One approach would be the pattern in this question: Why do these type arguments not conform to a type refinement? , but it seems the Scala compiler can't handle it.

    Edit2 Ignore the above paragraph. As Dylan mentioned in the comments, for this diamond inheritance situation, Scala nicely ensures the consistency of the type E. For example, the following compiles fine:

    trait GraphOps { type E; type V }
    trait WeightedGraphOps extends GraphOps { def f(e: E) }
    trait DirectedGraphOps extends GraphOps { def e: E }
    object GraphOps{
      def Dijkstra[V0, G <: (WeightedGraphOps{type V = V0}) with (DirectedGraphOps{type V = V0})] (graph:G, start:V0) = {
        graph.f(graph.e)
      }
    }
    
    0 讨论(0)
  • Why is it supposed to be Edge? If you look at the declaration of Dijkstra, you'll see that none of the parameters make reference to E: (graph:G, start:V). So Scala has a clue what G is supposed to be, and what V is supposed to be. There's no parameter making reference to E.

    0 讨论(0)
提交回复
热议问题