Categorising Dialog predicates

Hi,

I’m reading through the documentation to get the hang of Dialog, and it seems that there are three kinds of predicate:

Unchangeable functional predicates like (room X), or (not here X) where values are adjusted by creating truth rules.
Dynamic predicates like (X is Y Z) where values are adjusted using (now).
Side effective predicates like (appearance X) which do something, commonly printing something, when invoked, but do not contribute to the truth of the model

This is causing a bit of confusion with some things, eg if you wish to say that something is not edible but to not print the standard message, it seems you can’t add that message to (edible X) but must instead add it to a verb handler for X.

Is there a clear list of which is which and is there any consideration of using a syntactic convention to distinguish them (eg, perhaps different types of form bracket as in ZIL?)

1 Like

On a similar note - could a rule be added that a predicate cannot be Multi-Queried unless it is declared that way (possibly by declaring the rule with the preceding *)?

There’s several spots in the documentation where the programmer is asked to remember that a particular predicate is multi-queried by the library and therefore most perform multi-queries itself - this is similar to the difference between member and memberchk in Prolog. However, it’s kind of awkward that if the developer forgets and doesn’t use an inner multi-query then the complier essentially slips a red cut into the program with all the attached risks.

Would it damage things to ensure that the prototype for a multi-query rule must include a signal that it’s a multi-query rule?

Also a small standard library bug. inventory lists the objects you’re carrying even if it’s dark, but trying to drop one of them in the dark will give the message “(Object) isn’t here.”

I agree that the same syntax seems to refer to several different things. On one hand I welcome this simplicity (it’s way easier to remember that everything starts and ends with brackets, as opposed to Inform 7 where every different type of function has it’s own syntax - think events, type ofs, things, values that vary, rulebooks, etc), and on the other hand it can get confusing - there’s no reason why, except for convention, you couldn’t do (X is a room) (vs (room X)) or (X is empty) (vs (empty X)).

Though these conventions do indicate the type of predicate in themselves, somewhat. Perhaps a way to think about it is that every predicate is equal, and “unchangeable functional predicates” (I believe the Dialog manual refers to these as “traits”) are not so different from “dynamic predicates”, and a predicate is at once both what can be traditionally thought as a function and a boolean (with varying gradients along this line, “side effective predicates” behaving more of a function, while “unchangeable functional predicates” (traits) behaving more like a boolean. I believe (now) (X is Y Z) is a special syntax that negates the former predicate, replacing it with a new boolean (~(#cat is #in #bag) (#cat is #in #box)) .

There’s something similar in the fact that variables are interchangeable with objects in the syntax (e.g. you can say (prevent [eat $Obj]) as well as (prevent [eat #orange])), in that you’re really making a predicate for a whole class of objects. Dialog is pretty interesting in this regard (and in my opinion it makes it slightly more enjoyable, if not hard to begin thinking about).

Though perhaps you’re right that the manual should be clearer on the differences between these things and how Dialog handles them internally. It’s the least we could do to ease the onboarding process for new authors. Right now the best we can do is guess. @Ift, could you consider this?

2 Likes

It’s more to do with ease of readability and understandability.

For example, you have to remember that

(#player eats meat) Tasty!

Doesn’t define a predicate, even though it looks exactly like one, because “eats meat” is taken as a per-object flag and thus a dynamic predicate, and not allowed to be side effective, so instead “Tasty!” is printed during load. But

(edible #pie) Tasty!

does work, but doesn’t make sense, because it prints Tasty! whenever an edibility check is made on the pie.

On the other hand, saying that dynamic and procedural predicates use different brackets, clears this up very quickly.

<#player eats meat> Tasty! can be spotted quickly as an error if < means a dynamic predicate, and [edible #pie] Tasty can be spotted quickly as an error if [ brackets debar side effects. Having to start a multiqueried predicate with *( instead of ( avoids needing to debug later issues if a subquery isn’t multi.