Skip to content
This repository was archived by the owner on Jun 5, 2023. It is now read-only.
This repository was archived by the owner on Jun 5, 2023. It is now read-only.

class PG[val t: RDF](node: t.Node, gr: t.Graph)? #133

@bblfish

Description

@bblfish

Porting banana-rdf to Dotty I stumbled across the removal of type projections which we use a lot and I think wisely.
We have an RDF trait which specifies all the main types declared by the W3C specs.

trait RDF {
   type Graph
   type Triple
   type Node
...
}

This allowed us to abstract the various Java (Jena from HP/Apache and RDF4J from IBM) and Javascript implementations by defining functions between the types and mapping those methods for each implementation in an RDFOps[Rdf]. This allows one to write libraries in an implementation agnostic way by implicitly importing an ops. One can then write code once and change implementation with one line.

We could thus create classes like the following:

class PointedGraph[Rdf<:RDF](pointer: Rdf#Node, graph: Rdf#Graph)

With the removal of projections in dotty I tried this instead:

class PointedGraph(using val rdf: RDF)(pointer: rdf.Node, graph: rdf.Graph)

which is quite close. The implicit has to come first to be used to select the types in the arguments. Apart from not being usable in case classes it also forces the following usage

new PointedGraph()(p, graph)

Note the extra parenthesis that can't be dropped even using a companion object's apply method.
So this made me think that something along the lines of the following could be done:

case class PointedGraph[val rdf: RDF](pointer: rdf.Node, graph: rdf.Graph)

This makes sense since the role of rdf is to give us access to the dependent types and I could then imagine that type inferencing could allow one to write val pg = PointedGraph(p,g) giving us the same ease of use as before.

There is perhaps another reason to think of doing this, which I came across attempting to rewrite the library. I translated the PointedGraphs class to:

class PointedGraphs(using val ops: RDFOps)(
  val nodes: Iterable[ops.Rdf.Node], 
  val graph: ops.Rdf.Graph
) extends Iterable[PointedGraph] {
  import ops.Rdf

But then this fails in a method that returns a PointedGraph, because the dependent type is no longer explicitly tracked. I tried fixing it by writing the following in my IDE

def /(p: Rdf.URI): PointedGraphs(ops) = { //<-- a dependent argument type
    val ns: Iterable[Rdf.Node] = this flatMap { (pointed: PointedGraph) =>
      import pointed.pointer
      ops.getObjects(graph, pointer, p)
    }
    new PointedGraphs()(ns, graph)  // <-- needs an extra () for the implicit
  }

Doing that lead me to discover discover the very interesting Dependent Argument Types PR. (ie. it's an intuitive thing to do).

Following on the proposal here, perhaps the following may be more intuitive:

def /(p: Rdf.URI):  PointedGraphs[ops] 

This is not a feature request, rather a suggestion inquiry, as I don't yet feel
I have a complete grasp of the whole of dotty to be sure I have not missed
some important feature.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions