Re: ISSUE-58: the simple solution to inlined membership

On 14 May 2013, at 23:07, Steve Speicher <sspeiche@gmail.com> wrote:

> 
> 
> 
> On Mon, May 13, 2013 at 12:25 PM, Henry Story <henry.story@bblfish.net> wrote:
> Hi, 
> 
>       During today's teleconference discussing this issue I suddenly 
> realised that there is a  futher solution to those presented here, which 
> I think is both simpler and can be applied much more widely: that is to 
> all linked data.
> 
> Thinking about this a little more, I don't think it really makes it simpler.  If fact, it makes the simplest case harder, typical case harder, inlined case better in some ways but introduces ambiguity into what the resource URI is.  Let me try to explain....
> 
>  
>   So first of all it turns out that there are good arguments for the use cases
> of A and B/C . The current proposals end up requiring the creation of two
> new relations. This is problematic because linked data consumers need to
> know about these relations. That is a Linked Data Client instead of just having
> 
> This is not true, they do NOT need to know about these new relations.  They only need to know about these IF they care if the member resource (MR) has been fully inlined.  I have and can think of many cases where I don't really need to know this.
>  
> to make the following query on an LDPC named ldpc
> 
>    val members =  ldpc/rdf.member
> 
> It now has to also do something like the following
> 
> val members = if ( (ldpc/membersInlined).contains("true") )  
>                               ldpc/ldp.memberInlined
>                          else {
>                              val local = ldpc/ldp.memberInlined 
>                              val remote = (ldpc/rdf.member - local).map( _.follow )
>                              local union remote
>                           }
> 
> ( much more complex that this to tell you the truth )
> 
> To be clear, a client only needs to do this if its primary use case is optimizing fetching of complete MRs.  If the client does not care about them being inlined or optimizing, it would just ignore the additional triples.
>  
> What is problematic about this is that it would only work for LDPCs, and one could
> easily imagine that each LDP service would develop its own version making code
> unecessarily difficult.
> 
> But I have to explain the simple solution to make it clear why I can use "unecessarily 
> difficult": the simple answer is that RDF already comes with the tools to make distinguish
> nodes one can follow and nodes one cannot: the blank node! So I propose that for resources
> where all the data is contained locally you do the following
> 
> <> a ldp:Container;
>    rdf:member [ atom:title "Atom Robots Run Amock" ;
>                          atom:summary "Atom Robots having drunk a triple espresso semantic powerade....";
>                          atom:content " ...." ; 
>                          atom:id "http://news.example/2013/05/13/atomRobots"^^xsd:anyURI;
>                          atom:updated "2013-05-13..."^^xsd:dateTime;
>                        ],
>                        [ atom:title "Semantic Revolution in the Blogosphere";
>                          atom:summary "it all makes sense!";
>                          atom:id "http://news.example/2013/05/12/semanticRevolution"^^xsd:anyURI;
>                          ...
>                        ] .
> 
> So here it is no way to follow the LDPC members, and the ids are not URIs in use
> either. If you do want to also allow people to follow the links you can use owl:sameAs or perhaps
> the rel=self relation from atom
> 
> <> a ldp:Container;
>    rdf:member [ atom:title "Atom Robots Run Amock" ;
>                          atom:summary "Atom Robots having drunk a triple espresso semantic powerade....";
>                          atom:content " ...." ; 
>                          atom:self <atomRobots>;
>                          atom:updated "2013-05-13..."^^xsd:dateTime;
>                        ],
>                        [ atom:title "Semantic Revolution in the Blogosphere";
>                          atom:summary "it all makes sense!";
>                          atom:self <semanticRevolution>;
>                          ...
>                        ] .
> 
> 
> Finally for members where the data should be followed first rather than later
> 
> <> a ldp:Container;
>    rdf:member <atomRobots>,  <semanticRevolution> .
> 
> # a bit of extra data for people arriving on this resource using simpler tools...
> 
> <atomRobots> atom:title "Atom Robots Run Amock" ;
>                          atom:summary "Atom Robots having drunk a triple espresso semantic powerade....";
>                          atom:updated "2013-05-13..."^^xsd:dateTime .
>                        
> <semanticRevolution> atom:title "Semantic Revolution in the Blogosphere";
>                          atom:summary "it all makes sense!" .
> 
> The advantage of this is that one can write clients that follow links automatically ( with 
> cleverly built cashes to avoid fetching ontologies such as foaf or DC of course ) 
> so that as far as possible they always  go to the source of the data, where the information
> is defined. When a server does not wish this to happen the server can simply use the blank
> node thereby simply stopping the possiblity of getting further information!  The atom:self type
> relation or owl:sameAs then gives a way for the server to express that all the data is available
> remotely at that location.  
> 
> As stating the simplest scenario, where I have a client that doesn't care about inlining...it now needs to be concerned with blank nodes and interpreting owl:sameAs and/or atom:self.  owl:sameAs is where the ambiguity comes in, if my MR already has multiple statements saying it is owl:sameAs <foo>, <bar>, <taco>, ... then which one is the member of the container?  I know you would say "they all are, since they are the same".  This adds complexity to a simple client, I think we could all agree.

yes, that is why I only suggested owl:sameAs. The AtomPub atom:self relation
makes a lot more sense here.

> 
> There are cases for my simple client where it can get by just fine with partial information to drive a number of use cases: driving a selection box, understanding the kinds of resources, etc.
> 
> <> a ldp:Container;
>     rdfs:member <bug1>, <bug2>.
> 
> <bug1> a bugs:Bug;
>     dcterms:title "Server crashes hard";
>     owl:sameAs <http://jira.apache.org/issue-14>.
> 
> <bug2> a bugs:Bug:
>     dcterms:title "Button not aligned";
> 
> Now if server decides that it wants to provide a the <bug1> MR inlined, it would have to change to:
> 
> <> a ldp:Container;
>     rdfs:member <bug2>,
>       [ a bugs:Bug;
>         dcterms:title "Server crashes hard";
>         :state "Completed";
>         owl:sameAs <http://jira.apache.org/issue-14>, <bug1>. ].
> 
> <bug2> a bugs:Bug:
>     dcterms:title "Button not aligned";

without the owl:sameAs this would give us:

<> a ldp:Container;
    rdfs:member <bug2>,
      [ a bugs:Bug;
        dcterms:title "Server crashes hard";
        :state "Completed";
        atom:self <bug1>;
        owl:sameAs <http://jira.apache.org/issue-14>. ].

<bug2> a bugs:Bug:
    dcterms:title "Button not aligned";


> 
> From this example, it seems like an extreme shift from simply doing (Option B):
> 
> <> a ldp:Container;
>     rdfs:member <bug1>, <bug2>;
>     ldp:memberInlined <bug1>.
> 
> <bug1> a bugs:Bug;
>     dcterms:title "Server crashes hard";
>     :state "Completed";
>     owl:sameAs <http://jira.apache.org/issue-14>.
> 
> <bug2> a bugs:Bug:
>     dcterms:title "Button not aligned";
> 
> To me, inserting the extra statements that can be ignored by simple clients seems have the most advantages.


1. what is a simple  Linked Data client?

What simple client in LinkedData can afford not to make the distinction between a blank
node it can not follow and a link it can follow? That seems to me just basic linked data
plumbing. Client libraries that cannot do that are not really linked data clients. They would at most
be quick scripts to parse data for a particular service. And even they would need to 
know about blank nodes.

2. The Atom example

Atom is a widely distributed application that does things exactly as I propose. They
have a lot of clients that know how to parse things that way, and this has not
been a problem. 
  The nice thing about this example is that we are very close to the atom intuitions,
which means that people won't be surprised about this way of doing things, and we
don't need to invent a new relation. 

3. client code is simpler

[1] val members = ldpc/ldp.member  // get all the member pointed graphs
[2] val remote =  members.map { 
[3]        case PointedGraph(node,graph) if isURI(node) => node.fetch // select the pointers to remote graphs and fetch them
[4]        case pg => pg 
[5]    }

Your "simple" code would have to do [1] at least
Then it would need to do [2] and [3] also.  (It it needs to go fetch the remote graphs anyway )
It is [4] that is new here, with the if clause in [3]. But any LDP client needs to write those anyway,
whatever happens.

So everything your normal client needs is what I am proposing we need.

Your solution on the other hand requires any efficient client to also do two more searches on 
ldpc. One to find out if the member is ldp:memberInlined the other if the container has all
members inlined. 


>  
> 
> This way we have an answer that works for all LDP resources and we can write generic
> code without having to make special corner cases for each type of resource we come across.
> 
> 
> I'm not sure it is that simple with your approach and agree with the goals you state.  I believe the approach I illustrated, meets those goals as well.

I hope my arguments above have helped convince you that the answer is simpler :-)

> 
> - Steve
> 
> 
> Henry
> 
> On 30 Apr 2013, at 20:51, Arnaud Le Hors <lehors@us.ibm.com> wrote:
> 
>> Looking back at what has been said on this issue, I see several possible paths forward: 
>> 
>> Option A: Richard's original proposal (without all the details): 
>> 
>> Add to ldp:Container a boolean property which, when true, indicates that a complete description of all the members is inlined in the container document. 
>> 
>> Option B: 
>> 
>> Add to ldp:Container a property ldp:memberInlined which indicates the members for which a complete description is inlined in the container document. 
>> 
>> Option C: 
>> 
>> Add a boolean property ldp:memberInlined which, when true, indicates that a complete description of that member is inlined in the container document. 
>> 
>> Option D: 
>> 
>> Add a repeatable HTTP Header, such as X-Cacheable-for, which when set to a member URI means that a complete description of that member is inlined in the container document. 
>> 
>> 
>> Here are some examples for each options: 
>> 
>> Option A: 
>> 
>> # The following is the representation of
>> #                  http://example.org/netWorth/nw1
>> @prefix dcterms: <http://purl.org/dc/terms/>.
>> @prefix ldp:      <http://www.w3.org/ns/ldp#>.
>> @prefix o:       <http://example.org/ontology/>.
>> 
>> <>
>>   a o:NetWorth, ldp:Container;
>>   ldp:membershipPredicate o:asset;
>>   o:asset <a1>, <a2>; 
>>    ldp:membersInlined true.
>> 
>> <a1>
>>    a o:Stock;
>>    o:value 10000.
>> <a2>
>>    a o:Bond;
>>    o:value 20000. 
>> 
>> 
>> Option B: 
>> 
>> # The following is the representation of
>> #                  http://example.org/netWorth/nw1
>> @prefix dcterms: <http://purl.org/dc/terms/>.
>> @prefix ldp:      <http://www.w3.org/ns/ldp#>.
>> @prefix o:       <http://example.org/ontology/>.
>> 
>> <>
>>   a o:NetWorth, ldp:Container;
>>   ldp:membershipPredicate o:asset;
>>   o:asset <a1>, <a2>; 
>>    ldp:memberInlined <a1>, <a2>.
>> 
>> <a1>
>>    a o:Stock;
>>    o:value 10000.
>> <a2>
>>    a o:Bond;
>>    o:value 20000. 
>> 
>> Option C: 
>> 
>> # The following is the representation of
>> #                  http://example.org/netWorth/nw1
>> @prefix dcterms: <http://purl.org/dc/terms/>.
>> @prefix ldp:      <http://www.w3.org/ns/ldp#>.
>> @prefix o:       <http://example.org/ontology/>.
>> 
>> <>
>>   a o:NetWorth, ldp:Container;
>>   ldp:membershipPredicate o:asset;
>>   o:asset <a1>, <a2>.
>> 
>> <a1>
>>    a o:Stock;
>>    o:value 10000; 
>>    ldp:memberInlined true.
>> <a2>
>>    a o:Bond;
>>    o:value 20000; 
>>    ldp:memberInlined true. 
>> 
>> Option D: 
>> 
>> # The following is the representation of
>> #                  http://example.org/netWorth/nw1
>> @prefix dcterms: <http://purl.org/dc/terms/>.
>> @prefix ldp:      <http://www.w3.org/ns/ldp#>.
>> @prefix o:       <http://example.org/ontology/>.
>> 
>> <>
>>   a o:NetWorth, ldp:Container;
>>   ldp:membershipPredicate o:asset;
>>   o:asset <a1>, <a2>.
>> 
>> <a1>
>>    a o:Stock;
>>    o:value 10000.
>> <a2>
>>    a o:Bond;
>>    o:value 20000. 
>> 
>> HTTP Headers: 
>> X-Cacheable-for: http://example.org/netWorth/nw1/a1 
>> X-Cacheable-for: http://example.org/netWorth/nw1/a2 
>> 
>> Comments anyone? 
>> --
>> Arnaud  Le Hors - Software Standards Architect - IBM Software Group
> 
> Social Web Architect
> http://bblfish.net/
> 
> 
> 

Social Web Architect
http://bblfish.net/

Received on Wednesday, 15 May 2013 08:16:22 UTC