A while ago, I blogged about NOTEXIST having a bad semantics. Essentially, the semantics of the NOTEXIST operator wreaked havoc on subsequent blocks because they were all negated as well. Not good.

Reading Doedens' PhD thesis, I stumbled across a potential solution. Doedens has something called a "wrap block" which is essentially a block that can be used to match any monad set which is not necessarily known as an object or gap in the database. Doedens' proposed semantics of the "wrap block" is hard to implement because he uses the existence of a set within the powerset of all monads of the Substrate as the basis for what to take as the substrate. As we all know, the powerset of a set M gives rise to 2\^|M| subsets. That's a little steep to search through... Hence I would propose a simpler semantics. With that in mind, some explanation...

In my version of the "wrap block", the semantics is such that:

  1. The Universe of the wrap block is the Universe of the context, minus the monads from the beginning of the context's Universe up to and including whatever was the previous block's last monad. The Substrate is defined analogously on the context's Substrate. If there is no previous block, the Universe and Substrate of the wrap block are identical to those of the context.
  2. There can be no power block right before a wrap block. (OK, that was syntax, not semantics). The reason is so as to keep the implementation simple -- no more, no less; there is no theoretical reason why there could not be a power block before the wrap block.
  3. The result of a wrap block is one of three: Either a) a NIL_mo (a failed Matched Object), or b) an EMPTY_mo (an empty MatchedObject), or c) a ID_M_mo (a Matched Object with a set of monads and no id_d, like the Matched Objects arising from a gap block).
  4. The result is NIL_mo if and only if the inner sheaf is a failed sheaf. The result is EMPTY_mo if and only if the inner sheaf is a non-failed, empty sheaf. The result is an ID_M_mo if and only if the inner sheaf is a non-failed, non-empty sheaf. The set of monads M associated with the ID_M_mo is the big-union of all sets of monads within the inner sheaf.

With that in mind, let's see what happens if you implicitly wrap any NOTEXIST block in a "wrap" block.

  1. The Universe and Substrate of the NOTEXIST block will be restricted to start at whatever the previous block's last monad was, plus 1 monad, but they will continue to the end of the context. This means that the NOTEXIST block will be "looked for" within the entire Substrate, except everything that goes up to and including the previous block.
  2. If the NOTEXIST block matches anything, the inner sheaf of the wrap block will be a failed sheaf. Hence the wrap block will give rise to a NIL_mo, meaning it failed. The whole block_string will fail. This is as it should be, since we do not want there to be anything which matches the NOTEXIST block.
  3. If the NOTEXIST block does not match anything, the inner sheaf of the wrap block will be a non-failed, empty sheaf. Hence the wrap block will yield an EMPTY_mo. This means that any block following right after the (implicit) wrap block will be attempted to be matched starting at the previous block's last monad plus 1, as if the NOTEXIST block had not been there. The NOTEXIST block will essentially be a "zero-width" block, or a "negative zero-width lookahead assertion".

Here's an example:

[Clause [Phrase] NOTEXIST [Word surface="glue"] [Word surface="food"] ]

This would look for clauses within which there was a Phrase. Right after the end of this Phrase, there must be a word with surface="food". This is because the intervening NOTEXIST has "zero width". Moreover, starting at the end of the Phrase plus 1 monad, we look for the word "glue" within the Substrate up until the end of the Clause. If the word "glue" does not exist within that substrate, the whole query matches for that Clause. If the word "glue" does exist within that substrate, the whole query fails for that Clause.

Let's look at the semantics of this in terms of mathematics:

exists C in Clause restricted to Su: exists P in Phrase restricted to C: exists monad set M: exists W2 in Word restricted to C: P.last + 1 = W2.first and W2.surface="food" and M=Su\{Su.first..P.last} and for all W1 in Word restricted to M: W1.surface <> "glue"

The whole problem is side-stepped by postulating the existence of a monad set M, then defining that M on the basis of the previous block, then only thereafter postulating the "not exists" term (in the above example, this has become "for all"). That way, the "negation operator" becomes local in scope to the conjunctive term, not to the whole of the clause.

Another example, this time with two NOTEXISTS:

[Clause NOTEXIST [Word surface="glue"] NOTEXIST [Word surface="food"] ]

This would find all clauses within which neither the word "glue" nor the word "food" existed, in any order (OK, the last bit was a joke... there can be no ordering of things that do not exist...).

This would be, in FOL:

exists C in Clause restricted to Su: exists monad set M1: exists monad set M2: M1 = Su and M2 = Su and for all W1 in Word restricted to M1: W1.surface <> "glue" and for all W2 in Word restricted to M2: W2.surface <> "food"

Again, the "not exist" becomes a conjunctive term, not a quantifier that has scope over the whole of the rest of the clause.

There is still a restriction that must remain in place: If you place an "AS X" object reference declaration on a NOTEXIST block, you can only use it within the inner blocks of the NOTEXIST block, and not anywhere else.

I will try to implement this soon. Not the wrap block in general at first, but merely the "invisible" wrap block around a NOTEXIST block.

Update: The first logic-example has been fixed. S--> C.