Return-Path: X-Original-To: apmail-incubator-clerezza-dev-archive@minotaur.apache.org Delivered-To: apmail-incubator-clerezza-dev-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 86C9A7665 for ; Fri, 22 Jul 2011 14:46:30 +0000 (UTC) Received: (qmail 11503 invoked by uid 500); 22 Jul 2011 14:46:30 -0000 Delivered-To: apmail-incubator-clerezza-dev-archive@incubator.apache.org Received: (qmail 11431 invoked by uid 500); 22 Jul 2011 14:46:29 -0000 Mailing-List: contact clerezza-dev-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: clerezza-dev@incubator.apache.org Delivered-To: mailing list clerezza-dev@incubator.apache.org Received: (qmail 11419 invoked by uid 99); 22 Jul 2011 14:46:29 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 22 Jul 2011 14:46:29 +0000 X-ASF-Spam-Status: No, hits=-0.0 required=5.0 tests=RCVD_IN_DNSWL_LOW,SPF_NEUTRAL X-Spam-Check-By: apache.org Received-SPF: neutral (nike.apache.org: local policy) Received: from [209.85.216.47] (HELO mail-qw0-f47.google.com) (209.85.216.47) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 22 Jul 2011 14:46:22 +0000 Received: by qwh5 with SMTP id 5so1268108qwh.6 for ; Fri, 22 Jul 2011 07:46:00 -0700 (PDT) MIME-Version: 1.0 Received: by 10.229.221.12 with SMTP id ia12mr1298552qcb.14.1311345959674; Fri, 22 Jul 2011 07:45:59 -0700 (PDT) Received: by 10.229.219.71 with HTTP; Fri, 22 Jul 2011 07:45:59 -0700 (PDT) X-Originating-IP: [91.137.97.22] In-Reply-To: <2B4E7A08-BDD6-4EB0-A285-4008605B041E@bblfish.net> References: <4E26F65B.1090108@farewellutopia.com> <4E272720.1020709@farewellutopia.com> <4E27F0C4.3090602@farewellutopia.com> <06E9E2BC-1551-4249-975D-305D03DD49A2@gmail.com> <2B4E7A08-BDD6-4EB0-A285-4008605B041E@bblfish.net> Date: Fri, 22 Jul 2011 16:45:59 +0200 Message-ID: Subject: Re: Solution: taking context seriously was: A scala anti pattern -- looking for a name From: =?ISO-8859-1?Q?Reto_Bachmann=2DGm=FCr?= To: Henry Story Cc: scala-user , clerezza-dev@incubator.apache.org Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable X-Virus-Checked: Checked by ClamAV on apache.org I think the issue boils down to a design recommendation for code using the clerezza rdf library. Importing the members of an EzGraph as context makes perfectly sense on the clerezza console. The "coding" there is imperative, one want to see the results ideally after each line. Creating a block scoped to graph is clearly much less user-friendly [1]. Also having the content graph as default is very useful and shouldn't be sacrificed for enforcing good design in another context. Opening and being in the context you most often want to be is not only handy for rapid queries, the shell is a triple sink where new triples can be added to the clerezza instance simply by writing them down in a syntax similar to the turtle or the n-triples rdf format. Not having to call a method on some nodes on the graph makes a big difference in terms of usability. In program code that as opposed to the commands on the shell is deigned to last and which isn't executed while you type it is better design practice to instantiate an anonymous subclass of EzGraph (which could be renamed or aliased as context as in your examples) rather than importing the members (methods and implicts) of an instance. Your anti-pattern example shows horrible code that you can write using the library, you could very well write a similar example using the java transaction api. The clerezza API however supports better coding practices and the documentation shall focus on these, changing the api to enforce these better practice would be at high costs for those using the api interactively on the shell or in scripts. Reto 1. the clerezza faq entry on how to reset the password shows an example real-life usecase for switching graph-context on the console On Fri, Jul 22, 2011 at 10:12 AM, Henry Story wro= te: > Great I think I have the solution now. > This thread [1] has helped a lot to make clear the use of context, why > imports that bring behaviour with them > are confusing (they loose context) but =A0also why it was tempting to use > them. It turns out then that one has to take > context seriously, and rewrite the library in order not to have to use th= e > confusing implicit imports whilst making code > as easy to read as if they were there (because they will be). =A0Ie: we c= an > use those imports but make them explicit, > without ever writing them. If that sounds confusing, it turns out not to = be > in practice, as the following code shows. > One can easily create two graph: > val ez1 =3D new context { > b_("reto") -- FOAF.name --> "Reto Bachman-Gm=FCr".lang("rm") > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=A0-- FOAF= .homepage --> > "http://bblfish.net/".uri > }.graph > val ez2 =3D new context { > b_("hjs") -- FOAF.name --> "Henry Story" > }.graph > but one can then easily continue writing in each graph, in a layered way > without confusion > new context(ez1) { > =A0 =A0 =A0=A0=A0b_("reto") -- FOAF.interest --> "scala" > } > new context(ez2) { > =A0 =A0 =A0=A0=A0b_("hjs") -- FOAF.homepage --> "http://bblfish.net".uri > } > The context protects the blank nodes in the context, so they can't jump f= rom > one context to the other > (well they can because the framework at the top is leaky - but that is > something for another day), > the subject of the context is very clear too, and one can even add method= s > to this context. > The only thing that is odd from a technical perspective is that this will > create a bunch of classes on > each "new". > =A0 =A0So I must admit without the discussion I would not have walked dow= n this > path, which seems like a good one. > Henry > The commit for this is here: > > =A0https://github.com/bblfish/clerezza/commit/6c95c934f15b96c753e3e8eb091= 482ccaf1b5e32 > The files to look at are the new context class > > =A0https://github.com/bblfish/clerezza/blob/6c95c934f15b96c753e3e8eb09148= 2ccaf1b5e32/parent/rdf.scala.utils/src/main/scala/org/apache/clerezza/rdf/s= cala/utils/context.scala > But especially the test class that has many examples of usage > =A0=A0https://github.com/bblfish/clerezza/blob/6c95c934f15b96c753e3e8eb09= 1482ccaf1b5e32/parent/rdf.scala.utils/src/test/scala/org/apache/clerezza/rd= f/scala/utils/EzMGraphTest.scala > [1]=A0https://groups.google.com/d/topic/scala-user/IsJ1yXjd2lw/discussion > On 21 Jul 2011, at 21:32, Henry Story wrote: > > On 21 Jul 2011, at 15:32, Rex Kerr wrote: > > But anyway, I am less concerned about the library than about standard use > cases.=A0 Would anyone want to use different graphs?=A0 If yes, selecting= them > based on sort-of-hidden context is unwise.=A0 Henry's example made it loo= k > like using different graphs was a pretty reasonable thing to do.=A0 And n= ote > that a one-character variable is a lot more informative than a zero-varia= ble > character: > > =A0 graphContext(definitionsPreamble.currentGraph) { g =3D> > =A0=A0=A0 whatever -- g(edgelabel) --> soandso > =A0 } > > Now you have, with a couple extra characters per call, documentation of w= hat > the relevant context is. > > Thanks Rex. This discussion has helped me also see how it could have seem= ed > so > attractive for Reto to want to use=A0this import behaviour. > We start from something very nice and simple we can all agree on (pretty > much) > =A0 =A0val g =3D new EzMGraph { > =A0 =A0 =A0 =A0 =A0reto -- FOAF.knows --> ( bnode --FOAF.name --> "henry"= ) > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 -- FOAF.knows --> =A0danny > =A0 =A0} > This is both nice and readable, efficient and context bound. (And this wa= s > one of Reto's early ideas.) > So bnode above is a method that returns a GraphNode (as defined in [1]) > =A0using the graph itself. Ie the above is equivalent > to > =A0 =A0 val g =3D new EzMGraph { > =A0 =A0 =A0 =A0 =A0node(reto) -- FOAF.knows --> ( this.bnode -- FOAF.name= --> "henry" > ) > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0-- FOAF.knows --> = =A0danny > =A0 =A0 =A0} > The good thing is that the result of ( this.bnode -- FOAF.name --> "henry= " ) > is a GraphNode with the same > backing graph as EzMGraph, so there is no copying of information from one > graph to another to do (which due > to blank nodes in the semantic web, might end up being more complex than = one > may think) > Now if one wanted to fill the graph with information from another context= , > one would want a notation > more like > =A0 =A0 val retoNd =3D g.node(reto) > =A0 =A0 for (p <- people) { =A0retoNode -- FOAF.knows --> ( p -- FOAF.kno= ws --> me > ) } > ie for a list of people assert that Reto knows them and that they also kn= ow > me . > But here if p were say a URI for a person, then one would be creating in = the > code > =A0 =A0( p -- FOAF.knows --> me ) > an new GraphNode each time backed by a different empty graph, because of = the > implicit > =A0 =A0 implicit def =A0resource2graphNode(res: Resource) =3D new RichGra= phNode(res, > new SimpleMGraph()) > This will feel wasteful, when compared to the import solution that Reto h= ad > proposed where all > resources end up being transformed to GraphNodes with the same backing > graph. > =A0 =A0With the code as it stands now in [1] one can still do that. Then = one > would have > =A0 =A0import g.node > =A0 =A0for (p <- people) { =A0retoNode -- FOAF.knows --> ( p -- FOAF.know= s --> me > ) } > =A0 =A0So now each p will be transformed into a GraphNode using the same = store > as g. > Of course, this is nothing more than one would have had one written > =A0 =A0=A0 =A0for (p <- people) { =A0retoNode -- FOAF.knows --> ( g.node(= p) -- > FOAF.knows --> me ) } > It is more repetitive, and will be even more so the longer the code. But = I > still prefer it to the > import above (for the moment). One feeds that something needs to be > delimited more clearly. > So perhaps the subclassing solution is the good one again for this > =A0 =A0 new EzMGraph(g) { > =A0 =A0 =A0 =A0 =A0=A0for (p <- people) { =A0retoNode -- FOAF.knows --> (= p -- FOAF.knows > --> me ) } > =A0 =A0 } > =A0 =A0 is not that much more verbose that then import version, but it do= es make > it clear where the graph > is delimited. With an type alias one could even make that look like this: > =A0 =A0new graph(g) { > =A0 =A0 =A0 =A0 =A0 for (p <- people) { =A0retoNode -- FOAF.knows --> ( p= -- FOAF.knows > --> me ) } > =A0 =A0} > =A0 =A0 Is that perhaps good enough? > =A0 =A0Henry > > > > > > > > > > > > [1]=A0https://github.com/bblfish/clerezza/blob/51be3b14a5488838d87094e814= 55ca806507b72b/parent/rdf.scala.utils/src/main/scala/org/apache/clerezza/rd= f/scala/utils/EzMGraph.scala > Social Web Architect > http://bblfish.net/ > > Social Web Architect > http://bblfish.net/ >