Nothing more happened in terms of design today, but I was pretty happy with the progress nonetheless. I've changed the structure of the code back into a working system, and made a start on the code for cardinality.
By moving the recognition of the
kowari:is predicate out into the parser, it has solved several problems, and made the code a lot simpler. These benefits are carrying through to the other constraints, so the design change seems to be a good one.
Using a factory method to create the appropriate type of constraint is the only part of the code which is doing a lot of
if()...else statements, and even this is reasonably simple. More importantly, because the predicates are being recognised at parse time instead of execution time, there is no need to have the magic predicates stored in the database on initialization, so they can be recognised while resolving the constraint. With the advent of different database back-ends with the new Resolver SPI, it won't always be possible to add magic predicates to a database, so this change turned out to be quite important anyway.
There are other advantages as well. Checking of the constraint parameters used to be done on the server while the constraint was being evaluated, but now this is done by the constructors of the constraints. This catches problems earlier, and simplifies the execution code on the server. Finally, the fact that AN has already changed the
ConstraintResolver to using the visitor pattern has meant that new constraint types can be introduced trivially, simply by adding a
visit() method with a parameter of the constraint type required.
Moving over to this arrangement removed 29 lines of
kowari:is code from a
visit() method which was handling all types of constraints, and added a one line
visit() method which accepts a
ConstraintIs class. It's simpler, easier to read, does not involve myriad
if() statements, and is far more maintainable.
ConstraintIs class was trivial as well. It just extends the standard
ConstraintImpl class, with a few minor changes. The constructor does not need the predicate, as this is defined already by the nature of the class. It also does checks on the types of the data. At this point we only permit
kowari:is to be used with a variable subject and a fixed-value object, so the constructor can check for these as well. Finally, a couple of convenience accessor methods return the value and the variable without the need to cast.
Chang and Lee
At Bob's suggestion, I've been reading Chang and Lee's Symbolic Logic and Mechanical Theorem Proving. I'm only up to chapter 4, but it has proven to be an easy read so far.
Predicate logic is so applicable to our querying that I'm a little ashamed at not having read it already. Admittedly, so far the book has been about verifying the truth of a formula, rather than finding all "interpretations" (RDF statements) which fit it, but it is still talking about exactly the kind of things our queries need to do. SR was all over this stuff when the query layer was designed, but I realise now that I should have known it as well. Fortunately I was working on the datastore layer at that time, but I should still have learned about formal predicate logic when I first started working on optimizing queries. Oh well, better late than never.
Thursday, August 12, 2004