rdfjs / types

Authoritative TypeScript typings for all RDFJS specifications

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Align on convention for quoted triples.

jeswr opened this issue · comments

Currently there is no convention on how to handle the graph term in quoted triples when implementing the RDF-star CG spec.

In my view there are 3 ways options of how to go about this without introducing any new terms:

  1. Make the graph term optional in RDFJS quads, and do not include it in quoted triples (this would be breaking) or allow the graph term to be set to null (also breaking).
  2. Agree that all quoted triples should be represented as RDFJS quads with the DefaultGraph set for the graph term
  3. Agree that all quoted triples should be represented as RDFJS quads with the graph term the same as that of the top level quad.

To give a concrete example then if we have the following nquads statement

<<:a :b :c>> :p :o :g

it would, in RDFJS, become

under option 1

quad(
   quad(
      namedNode('http://example.org/a'),
      namedNode('http://example.org/b'),
      namedNode('http://example.org/c'),
      null,
   ),
  namedNode('http://example.org/p'),
  namedNode('http://example.org/o'),
  namedNode('http://example.org/g'),
)

under option 2 (and as is currently implemented in this PR)

quad(
   quad(
      namedNode('http://example.org/a'),
      namedNode('http://example.org/b'),
      namedNode('http://example.org/c'),
      DEFAULT_GRAPH,
   ),
  namedNode('http://example.org/p'),
  namedNode('http://example.org/o'),
  namedNode('http://example.org/g'),
)

under option 3

quad(
   quad(
      namedNode('http://example.org/a'),
      namedNode('http://example.org/b'),
      namedNode('http://example.org/c'),
      namedNode('http://example.org/g'),
   ),
  namedNode('http://example.org/p'),
  namedNode('http://example.org/o'),
  namedNode('http://example.org/g'),
)

Currently I have a PR open to support the current RDF-star specification in N3.js where I have gone with option 2 (see rdfjs/N3.js#311).

I think option 2 is the only correct one, since RDF-star only defines nested triples (= quads in the default graph), but doesn't define triples in named graphs.

Make the graph term optional in RDFJS quads, and do not include it in quoted triples (this would be breaking) or allow the graph term to be set to null (also breaking).

Note that the graph term is already option in the data factory, as it defaults to the default graph.

I would totally expect that quoted triples are in the same graph as the quad they are part of. Consider same quad in Trig*

GRAPH :g {
  <<:a :b :c>> :p :o
}

I would be very surprised if :a :b :c was in the default graph and not g

Looking at the spec now, I'm confused even more. Trig-star section says

RDF-star describes quoted triples, which are not necessarily present in any named graph, or within the default graph.

I find this sentence grammatically unclear. On the other hand, paragraph 2.1 states:

In RDF 1.1, an asserted triple is an element of the set of triples that make up an RDF graph. RDF-star does not change this except that an RDF-star triple can contain quoted triples. A triple can be used as an asserted triple, a quoted triple, or both, in a given graph.

This seems to corroborate my gut feeling that a quoted triple belongs to the same graph as the quoting triple. null could make sense but it is null so great by its nature


Bottom line, it is most certainly not DEFAULT_GRAPH IMO

GRAPH :g {
  <<:a :b :c>> :p :o
}

Note that the above does not mean that the triple :a :b :c exists (in :g or somewhere else). It means that the triple :a :b :c is the subject of a quad with predicate :p, object :o, and graph :g. But that triple does not necessarily have to be present in the dataset.

In order to indicate that a triple exists, it must be added to the dataset explicitly, without being quoted (or as well as being quoted). In that case, this triple could be added to a default graph or named graph. This could be done by repeating that triple, or by using the compact annotation syntax of Turtle-star and TriG-star.


AFAICS, the RDF-star spec does not say anything about quoting triples coupled with a named graph. Since we define triples as quads in the default graph, it makes most sense to me to use the default graph as a convention for quoted triples (which would require no breaking changes to the spec).

Note that the above does not mean that the triple :a :b :c exists

I would not like to make this thread philosophical, but clearly, that triple does exist, doesn't it? :)

Please look at the paragraph 2.1 quoted above. Now, with my emphasis

[...], an asserted triple is an element of the set of triples that make up an RDF graph. [...] A triple can be used as an asserted triple, a quoted triple, or both, in a given graph.

Quoted triples do not just float around like some phantoms. They are part of the very graph where they are quoted. I'd find any other way strange

I would not like to make this thread philosophical, but clearly, that triple does exist, doesn't it? :)

No, not at all, that's the whole point of quoted triples in RDF-star :-)
The statement Ruben says "elephants are yellow" does not imply that elephants are yellow.

[...], an asserted triple is an element of the set of triples that make up an RDF graph. [...] A triple can be used as an asserted triple, a quoted triple, or both, in a given graph.
Quoted triples do not just float around like some phantoms. They are part of the very graph where they are quoted. I'd find any other way strange

That part of the spec is referring to asserted triples, which are not the same as quoted triples.

Asserted triples are indeed part of the graph they are mentioned in, but quoted triples are not.

See https://w3c.github.io/rdf-star/cg-spec/editors_draft.html#quoted-triples

Fair enough on the philosophical part. That said, I did not mean to say that a quoted triple represents an assertion (truth). By itself it does exist, as in it has been stated...

For the second, let's look one more time:

A triple can be used as an asserted triple, a quoted triple, or both, in a given graph.

"Quoted triple"
"in a given graph"

I can't see how this can be misunderstood ;)

A triple can be used as an asserted triple, a quoted triple, or both, in a given graph.

To me, this means that a triple can occur in two places within a graph: either directly (as an asserted triple), or indirectly (as a quoted triple nested in another triple). It does not answer the question of which graph a quoted triple belongs to.

That question is i.m.o. trivial: a quoted triple belongs to the graph the triple belongs to; whether it is quoted or not does not matter. Thus the quad :a :b :c :x is a triple belonging to :x, and :a :b :c is a triple belonging to the default graph.

Whether we assert or quote those triples changes nothing about these facts. So << :a :b :c >> :p :o :g is a triple in :g about a triple in the default graph, just as << :a :b :c :x >> :p :o :g is a triple in :g about a triple in :x (written in a logical extension of RDF-Star).

So I would agree with @rubensworks that this is the only correct way to go about handling quoted triples as quads.

I agree with Ruben that (2) is the only semantically correct** and non-breaking solution. It is why I chose to implement it that way in N3.js

So << :a :b :c >> :p :o :g is a triple in :g about a triple in the default graph

I disagree - here << :a :b :c >> is just a term that is part of a triple. In the same way that :a is not a term in the default graph or in :g, if :a :p :o :g is a triple in :g.

Or to go back to Rubens example Ruben says "elephants are yellow". I am not saying that "elephants are yellow" is part of :g or the default graph. I am simply saying making the overall statement Ruben says "elephants are yellow" in :g and the terms within that statement cannot be placed into any graph.

Just as << :a :b :c :x >> :p :o :g is a triple in :g about a triple in :x

I also disagree on this point - RDF-star explicitly disallows quoted quad statements (hence why I am opening this discussion of what we should always do with the graph term). This statement simply does not make sense in the context of the current RDF-star spec and cannot be expressed in any of the syntaxes defined by the spec.

** Note that semantically I think it would be better to set the graph term to null, but for the sake of not breaking the current RDF/JS interface I think the value of the graph term should be set to DEFAULT_GRAPH with the understanding that we ignore the graph term in quoted triples as the quoted triple does not actually belong to a graph in its own right

@jeswr

So << :a :b :c >> :p :o :g is a triple in :g about a triple in the default graph

I disagree - here << :a :b :c >> is just a term that is part of a triple. In the same way that :a is not a term in the default graph or in :g, if :a :p :o :g is a triple in :g.

That is not what I meant, but you are entirely correct in the obvious way of interpreting the sentence. 🤦‍♂️ What I meant was that it is a triple about some other triple 'as if' the latter were asserted in the default graph. This to accentuate the contrast with a triple about a triple that could be asserted in some other (named) graph.

Just as << :a :b :c :x >> :p :o :g is a triple in :g about a triple in :x

RDF-star explicitly disallows quoted quad statements (hence why I am opening this discussion of what we should always do with the graph term). This statement simply does not make sense in the context of the current RDF-star spec and cannot be expressed in any of the syntaxes defined by the spec.

(also @tpluscode:) That is precisely why I added that that syntax should be interpreted as a logical extension of RDF-Star. 🙃 The reason I introduced this 'non-sensical' notation, is as an intuition-pump: it shows how one could think about the graph term of a quoted triple, in a way that is consistent with both the way we handled (asserted) triple -> quad conversions in the past, and the way we will likely handle (quoted) triple -> quad conversions in the future. That way, according to me, is to interpret all triples (quads without graph), as quads with the default graph.

On a less technical analogue:

  • "Elephants are yellow," is a statement without context, which we therefore interpret in the default context (to conclude that the speaker may have drunk too much).
  • "Elephants are yellow in my favorite childhood book," is a statement with a named context, which we can therefore interpret and evaluate in that named context (to conclude that the speaker had a pretty normal childhood book).
  • "Ruben says 'Elephants are yellow'," is a statement (without context) about a statement without context, which we therefore interpret in the default context for both the asserted and the quoted part (to conclude that Ruben may have drunk too much).
  • "Ruben says 'Elephants are yellow in my favorite childhood book'," is a statement (without context) about a statement with a named context, which we therefore interpret and evaluate in the default context for the asserted part and in the named context for the quoted part (to conclude that Ruben had a pretty normal childhood book).

From this we can conclude that interpretation of statements/triples works very intuitively when we use the default context /graph if a named context/graph is lacking, both in cases where the statement is asserted and in cases where it is quoted.

To repeat: in none of the above do I claim that quoted triples have to be triples currently asserted in the default graph! I simply indicate that, given that they are quoted (and thereby already not necessarily asserted), these triples can be seen as having the default graph, since that would be where they are evaluated.

I agree with all of the above @jeswr and @woutermont!

I agree that null would be somewhat a breaking change. Not only to n3 but the spec in general. I think that maybe this is an unanticipated consequence of RDF-*?

At least with null, however, it is clear that a quoted triple is different from asserted triple. Otherwise, I fear that it will be easy to make that mistake somewhere in code that a quoted triple "escapes" its usage and becomes asserted. But in which graph?

import { Quad, Quad_Graph, DatasetCore } from '@rdfjs/types'

let graph: Quad_Graph
let quotedQuad: Quad = // "elephants are yellow"
let assertedQuad: Quad = $rdf.quad(ex.ruben, ex.says, quotedQuad, graph)

let dataset: DatasetCore

// this is business as usual, as described by paragraph 2.1
dataset.add(assertedQuad)

// this should be illegal
dataset.add(assertedQuad.object)

Do you see the problem? The current spec does not prevent the quoted quad from becoming asserted? Either solution other that graph: null is wrong an may lead to subtle errors, where it becomes asserted in graph graph (option 2) or DEFAULT_GRAPH (option 3).

I conclude that I was initially wrong about choosing option 3 and more leaning towards option 1

I said above that the change is only "somewhat breaking". I'd argue that if current types and implementation do not follow the spec in that regard than a seemingly breaking change qualifies as necessary bug fix and are thus excusable :)

Having looked a little into the current types, I think a mistake to make Quad a Term. Maybe instead we should have brought back Triple? That would properly reflect the RDF0Star spec which as "quoted triples" and not "quoted quads"

diff --git a/node_modules/@rdfjs/types/data-model.d.ts b/node_modules/@rdfjs/types/data-model.d.ts
index 6fff406..5559fe0 100644
--- a/node_modules/@rdfjs/types/data-model.d.ts
+++ b/node_modules/@rdfjs/types/data-model.d.ts
@@ -10,7 +10,7 @@
  * @see DefaultGraph
  * @see BaseQuad
  */
-export type Term = NamedNode | BlankNode | Literal | Variable | DefaultGraph | BaseQuad;
+export type Term = NamedNode | BlankNode | Literal | Variable | DefaultGraph | BaseQuad | Triple;
 
 /**
  * Contains an IRI.
@@ -133,7 +133,7 @@ export interface DefaultGraph {
  * @see BlankNode
  * @see Variable
  */
-export type Quad_Subject = NamedNode | BlankNode | Quad | Variable;
+export type Quad_Subject = NamedNode | BlankNode | Triple | Variable;
 
 /**
  * The predicate, which is a NamedNode or Variable.
@@ -149,7 +149,7 @@ export type Quad_Predicate = NamedNode | Variable;
  * @see BlankNode
  * @see Variable
  */
-export type Quad_Object = NamedNode | Literal | BlankNode | Quad | Variable;
+export type Quad_Object = NamedNode | Literal | BlankNode | Triple | Variable;
 
 /**
  * The named graph, which is a DefaultGraph, NamedNode, BlankNode or Variable.
@@ -233,6 +233,8 @@ export interface Quad extends BaseQuad {
     equals(other: Term | null | undefined): boolean;
 }
 
+export type Triple = Omit<BaseQuad, 'graph'>
+
 /**
  * A factory for instantiating RDF terms and quads.
  */

Option 4: A quoted triple does not have graph at all.

At least with null, however, it is clear that a quoted triple is different from asserted triple. Otherwise, I fear that it will be easy to make that mistake somewhere in code that a quoted triple "escapes" its usage and becomes asserted.

I get the feeling that you have a fundamentally different view on what a triple is then I do. Semantically, at least, it is impossible to "escape usage": if a triple :a :b :c is quoted then it is a quoted triple, and if it is asserted then it is an asserted triple; if it is both quoted and asserted, it is both a quoted and asserted triple.

Naming variables quotedQuad: Quad or assertedQuad: Quad is misguided, since they are not yet in use. The former one only becomes quoted when used in the quad(ex.s, ex.o, quotedQuad, ex.g) constructor, and the latter one only becomes asserted when added to a dataset with dataset.add(assertedQuad). It should be perfectly possible to first create a triple, and only later decide to use it in one, both, or no way. Using the wrong variable in the wrong place is simply bad programming. Otherwise, we could take it to the extreme with the following, and introduce new types for every variable to prevent it being used elsewhere.

let base: int = 0
let toAdd: int = 3
let toSubstract: int = 2

// this should be illegal
let result: int = base + toSubstract - toAdd

The fact that "quoted quads" are not yet specified does not preclude it from being a valuable addition to this discussion. To the contrary, choosing to treat nested triples just like regular quads (instead of trying to make it behave like some totally different ephemeral object) keeps open the brigh future where we can say stuff like "Ruben says elephants are gray, but Ruben also says elephants are yellow ... in his favorite childhood book." Moreover, we could take all the things that Ruben says, and create a dataset from that, with possibly multiple graphs and a default graph.

Yes, thank you for complaining about variable naming and ignoring the merit.

Here's a revised snippet to remove the distraction:

import { Quad, Quad_Graph, DatasetCore } from '@rdfjs/types'

// Ruben says "elephants are Yellow"
let quad: Quad = $rdf.quad(
  ex.ruben, 
  ex.says, 
  $rdf.quad(ex.elephant, ex.color, "Yellow", ex.GraphB) 
  ex.GraphA
)

let dataset: DatasetCore

dataset.add(quad)
dataset.add(quad.object)

To rephrase: the last line is problematic because we don't know what graph the assertedQuad.object should belong.

The winning proposal is DEFAULT_GRAPH. But wait, the quad was created with GraphB as last argument. Finally, the quad is asserted in GraphA (this would be Option 3).

Which one is it then? This will inevitably lead to hard to find errors. My proposed change in types is to force the caller to explicitly assert in the graph they need:

let quad
let { subject, predicate, object } = quad.object

// to assert in GraphC
dataset.add($rdf.quad(subject, predicate, object, ex.GraphC))

That said, implementation-wise, little can potentially change.

I would implement quad in a way that actually strips the graph from a quad when using it as subject/object of another quad and throw when trying to assert such a triple directly in a call dataset.add(quad.object)

But if an existing implementation remains unchanged, it would still work. Only not prevent the hazard I described above, when used without types.

@tpluscode, the other 2/3 of my comment did address the 'merit' of your example. My conclusion just was that there is nothing special going on there except some programmer error. That remains the same for your new example: with the error now removed, I fail to see what is problematic about it.

[W]e don't know what graph the [quad.object] should belong. [A] The winning proposal is DEFAULT_GRAPH. But wait, [B] the quad was created with GraphB as last argument. Finally, [C] the quad is asserted in GraphA (this would be Option 3).

My reasoning has always been that it is quite evident that it is option B: the graph with which the quad was created. Arguments for that can be found in my earlier comments, but I will here add an example building on yours.

import { Quad, Quad_Graph, DatasetCore } from '@rdfjs/types'

const rubenSays: Quad[] = []

rubenSays.push($rdf.quad(ex.elephant, ex.color, "Gray", DEFAULT_GRAPH))
rubenSays.push($rdf.quad(ex.elephant, ex.color, "Yellow", ex.bookGraph))
rubenSays.push($rdf.quad(ex.elephant, ex.color, "Pink", ex.whatRubenSaid))

const statements: DatasetCore
const metaStatements: DatasetCore

for (stmt of rubenSays) { 

    const metaStmt: Quad = $rdf.quad(ex.ruben, ex.says, stmt, ex.whatRubenSaid)

    statements.add(stmt)
    metaStatements.add(metaStmt)

}

While the same quad is here used both as a quote and as an assertion, when interpreted with option B nothing about this is weird. The dataset of statements will contain precisely what Ruben said and in which context. One of those context is the default graph. The dataset of metastatements will include a single graph with one "Ruben says" statement for each statement Ruben said.

If we would simply drop all graph info and add the quoted graphs to the default graph of a dataset (option A), then the default graph would contain three contradications that were not there before (not in what Ruben actually said, and not in our claims about what he said). A similar situation happens when, as in option C, quoted triples are added to the graph of their containing statement (which would, in this case of two datasets, not even make sense).

My reasoning has always been that it is quite evident that it is option 2: the graph with which the quad was created.

I think you actually introduce yet another option, which is not the same as any of those proposed by @jeswr.

The point, made by @rubensworks comment

Asserted triples are indeed part of the graph they are mentioned in, but quoted triples are not.

Given that statement

:Ruben :says << :Elephant :color "yellow" >> .

we can only say that this dataset contains one quad. :Elephant :color "yellow" is a quoted triple which does not actually have any graph information. It is not asserted in any graph. This is part of the RDF* definition. Thus, we must ignore the graph property of any quoted triple. Because it is not a quad.

Look from the opposite direction. All of the options proposed here have one important problem, which can be shown when you consider parsing. When you parse this statement above, you cannot put :Elephant :color "yellow" in any graph because that information is not there.

This is exactly the same when you look at dataset.add(quad.object) from my code snippets. quad.object does not exist in any graph. Thus, the dataset (and types) must complain because an asserted triples is not a quad. And dataset is a set of quads...

I gathered my thoughts in #35

I think it's closest to Option 1 but rather than null, completely ignores graph of quoted triples. Whet implementations do is secondary, but at least aligns type declarations with spec

I think you actually introduce yet another option, which is not the same as any of those proposed by @jeswr.

My bad for being so ambiguous: my option numbers were refering to your options. I renamed them A, B and C.
My point is not new, however. It is the same as that of @rubensworks, if I am not very much mistaken.

[@rubensworks:] Asserted triples are indeed part of the graph they are mentioned in, but quoted triples are not.

[@tpluscode:] Given that statement :Ruben :says << :Elephant :color "yellow" >> . we can only say that this dataset contains one quad. :Elephant :color "yellow". is a quoted triple which does not actually have any graph information. It is not asserted in any graph. This is part of the RDF* definition. Thus, we must ignore the graph property of any quoted triple. Because it is not a quad.

I agree with all but the strikethroughs: a triple containing a quoted triple forms a dataset of one asserted quad; the quoted triple itself is not asserted in any graph, and one could call that "ignoring" its graph property. Interpreting this very securily, one must admit, however, that an ignored property is still a actual property. A quoted triple is thus very much a graph, whose graph property, even though ignored during assertion in a dataset, can be of very much use later.

An analogy again: the number has a "nested" number 2. This nested number is very much a number: even though we ignore it when we add the former to a set ({3²} contains only one number), leaving the latter out would entirely change the semantic value.

When you parse this statement above, you cannot put :Elephant :color "yellow" in any graph because that information is not there. This is exactly the same when you look at dataset.add(quad.object) from my code snippets. quad.object does not exist in any graph.

A triple never "exists in" a graph. It exists on its own and refers to a graph. And of course the information "is there", just as it "is there" for every asserted triple :a :b :c which we interpret as a triple in the default graph.

Just for clarity: I actually think I your suggestion (Option 1 but typed instead of null-valued) is the cleanest solution after mine. I am 100% sure though that in that case there will come a time in the near future where it will have to be reverted to add graphs to quoted triples again (e.g. when RDF-Star gets a graph-extension).

when RDF-Star gets a graph-extension

Is there an existing discussion on that matter to follow?

A triple never "exists in" a graph. It exists on its own and refers to a graph.

Maybe let's try to replace the term "exists" with "asserted in".

:a :b :c`

this triple is asserted in default graph.

graph :g {
  :a :b << :d :e :f >>
}

In Trig*, this is a triple asserted in graph :g. Its object is a quoted triple :d :e :f.
:d :e :f is not asserted in any graph (that we know). This is what I meant by "does not exist in any graph". By interpreting this one quad alone, the quoted triple it is not asserted in graph :g nor is it asserted in the default graph.

This is why it makes no sense to discuss code like

$rdf.quad(a, b, $rdf.quad(d, e, f, ???), g)

Anything which would replace ??? has no meaning. Even if you leave it out, which normally assumes default graph. As soon as an RDF/JS quad object gets used a a subject or object, it becomes an asserted triple in the context of the quoting quad.

An analogy again: the number has a "nested" number 2.

Yes, that is a good analogy, which explain how I think you mix semantics and syntax. When I have a set of numbers: [ 3² ], it contains only one number. 2 is an integral to that number, used in as part of a mathematical notation. Yes, syntactically 2 is still a number, of course. But semantically it is not an element of the set

Just a general remark: The RDF/JS specs are more open than one would expect after looking at the RDF spec. There was no big discussion about it, but I would always argue to keep it like this. It would be possible to generate invalid RDF(-star) data according to the data model spec, but that can be useful if you want to implement algorithms. For example, if you need an n-3 or n-4 tuple with an arbitrary term at the second position (predicate), it's possible to use the Quad and Dataset interfaces as defined in the RDF/JS spec, even if it's a literal. I think you are discussing only the data exchange use case, but please keep the data processing use case in mind, where a more open definition of the interfaces can be beneficial.

Thanks for giving your clarifying interpretation of the square analogy, @tpluscode, but it is mathematically simply not correct to say that the 2 in that example is only syntactically a number. Both parts of the syntax of refer to elements in the same domain of an algebraic structure.

This is what I meant by "does not exist in any graph". By interpreting this one quad alone, the quoted triple it is not asserted in graph :g nor is it asserted in the default graph.

Of course not, there we completely agree.

This is why it makes no sense to discuss code like $rdf.quad(a, b, $rdf.quad(d, e, f, ???), g). Anything which would replace ??? has no meaning. [my emphasis]

This is where we disagree. Just as the numeric example has operations where the power does not matter (e.g. modulo operations), RDF has operations where the graph of the quoted triple does not matter (e.g. adding the containing triple to a dataset). But just as the numeric example has operations where the power does matter (e.g. when calculating the result, or taking a root), RDF has operations where the graph of the quoted triple does matter: when I hear Ruben say :Elephant :color "yellow" :favBook ., and I want to transmit that information to you, :favBook indeed has no impact on exchange of that data, but it does very much impact your processing and understanding of it; which is basically a definition of "meaning".

I just encountered a little twist. Please consider this snippet and its output below

const $rdf = require('rdf-ext@1.3.5')
const N3 = require('n3')
const ex = require('@rdfjs/namespace@1.0.0')('http://example.com/')
const getStream = require('get-stream@5.0.0')

const yellow = $rdf.literal("Yellow")

const quadDG = $rdf.quad(ex.Simon, ex.says, 
  $rdf.quad(ex.elephants, ex.color, yellow, ex.G) // quoted in graph ?
)                                                 // asserted in default
const quadGD = $rdf.quad(ex.Simon, ex.says, 
  $rdf.quad(ex.elephants, ex.color, yellow), // quoted in default ?
  ex.G,                                      // asserted in graph
)
const quadGG = $rdf.quad(ex.Simon, ex.says, 
  $rdf.quad(ex.elephants, ex.color, yellow, ex.G),  // quoted in graph ?
  ex.H                                              // asserted in graph
)

const dataset = $rdf.dataset([ 
  quadDG, quadDG.object, 
  quadGD, quadGD.object, 
  quadGG,  quadGG.object ])

await getStream(dataset.toStream().pipe(new N3.StreamWriter({
  prefixes: { ex: 'http://example.com/' }
})))
@prefix ex: <http://example.com/>.

# quadDG
ex:Simon ex:says <<ex:elephants ex:color "Yellow" ex:G>>.
# quadGD.object
ex:elephants ex:color "Yellow".

ex:G {
  # quadGD
  ex:Simon ex:says <<ex:elephants ex:color "Yellow">>.
  # quadDG.object and also quadGD.object
  ex:elephants ex:color "Yellow"
}
  
ex:H {
  # quadGG
  ex:Simon ex:says <<ex:elephants ex:color "Yellow" ex:G>>
}

Notice that n3 preserved the graph information of all quoted triples. In that sense, the were all asserted in the correct graph. This also round-trips when these 3 asserted triples are parsed and those quoted added to the dataset.

In light of @bergos comment, if we agree that RDF/JS is a little more flexible than RDF* in accepting quoted quads and not only triples, I conclude that there is in fact nothing to do here. The graph information is taken at face value from the quoted.