I have been trying to see how far one could get with the simple trait RDF { ... }
listing all the types, and just using implicits like this:
class PointedGraph(using val rdf: RDF)(pointer: rdf.Node, graph: rdf.Graph)
which is not that far from what we had before
class PointedGraph[Rdf<:RDF]( pointer: Rdf#Node, graph: Rdf#Graph)
But this leads to the following problem with code like this:
class PointedGraphs(using val ops: RDFOps)(
val nodes: Iterable[ops.Rdf.Node],
val graph: ops.Rdf.Graph
) extends Iterable[PointedGraph] {
import ops.Rdf
The problem is that the extends clause Iterable[PointedGraph]
no longer has information about the RDF
type. And so we would need to pass that along in the definition and the method. I found that there was work in progress for Dependent Argument Types which would allow one to perhaps specify a type with an object argument PointedGraph(ops)
as shown below:
def /(p: Rdf.URI): PointedGraphs(ops) = { //<-- passing an object to a type
val ns: Iterable[Rdf.Node] = this flatMap { (pointed: PointedGraph) =>
import pointed.pointer
ops.getObjects(graph, pointer, p)
}
new PointedGraphs()(ns, graph) // note though that one needs an extra () for the implicit
}
All of this made me wonder if one should not have a way of expressing
class PointedGraph(using val rdf: RDF)(pointer: rdf.Node, graph: rdf.Graph)
like this
class PointedGraph[val rdf: RDF](pointer: rdf.Node, graph: rdf.Graph)
But in the meantime, I may have to add the type info back. This would make declaring objects double what they were before, as I would have both to keep track of the types and also pass around the implicit object.
class PointedGraph[Rdf<:RDF](using val rdf: Rdf)(
pointer: rdf.Node, graph: rdf.Graph)
That would make the code look a lot worse, and make me wonder if there is another way to do this.
I can only see a way out of this by wrapping all new classes in a trait containing the type info and instances. But I think that leads to the cake pattern.