While I love most of the Dialog standard library’s design, (refuse $) has always struck me as distinctly inelegant. This is the predicate that checks if an action is feasible, and rejects it (taking no time) if it’s not—if you try to take something intangible, for example, or examine something that’s not present.
Here’s how it’s currently implemented:
(refuse $Action)
*($Obj is one of $Action)
(object $Obj)
~(direction $Obj)
~(relation $Obj)
{
(when $Obj is not here)
(or) (when $Obj is out of reach)
}
In other words, reject the action if any of the objects in it is not present, or is out of reach.
If you want to adjust this for any particular action, you have to write your own (refuse $) rule with (just):
(refuse [look $ $Obj])
(just)
{
(when $Obj is not here)
(or) (when $Obj is out of sight)
}
And this means duplicating all the standard logic in your own rule:
(refuse [throw $Obj at $Target])
(just)
{
(when $Obj is not here)
(or) (when $Target is not here)
(or) (when $Obj is out of reach)
}
Which even the standard library sometimes gets wrong! If the standard library itself can’t keep this straight, it seems unfair to expect end users to.
So in the next release of Dialog, I’d like to replace this implementation. Here’s my proposal for how to do it.
First, (refuse $) will have three default rules:
(refuse $Action)
*($Action requires $Obj to be present)
(when $Obj is not here)
(refuse $Action)
*($Action requires $Obj to be seen)
(when $Obj is out of sight)
(refuse $Action)
*($Action requires $Obj to be touched)
(when $Obj is out of reach)
Each of these rules calls out to a new predicate:
($Action requires $Obj to be present)
*($Obj is one of $Action)
(object $Obj)
~(direction $Obj)
~(relation $Obj)
($Action requires $Obj to be seen)
*($Action requires $Obj to be present)
($Action requires $Obj to be touched)
*($Action requires $Obj to be seen)
That is:
- By default, an action requires all physical objects to be present
- By default, an action requires all (required to be present) objects to be seen
- By default, an action requires all (required to be seen) objects to be touched
Now these individual predicates can be overridden for more fine-grained control, without worrying about the others:
~([look $ $] requires $ to be touched)
([throw $Obj at $] requires $Obj to be touched) (just)
~([examine $] requires $ to be seen)
And you can still add your own refuse rules if you want:
(refuse [drop (sacred $)]) That's unthinkable!
Plus, you can check these predicates elsewhere, too!
(prevent $Action)
*($Action requires $Obj to be touched)
(burning $Obj)
You reach out for (the $Obj), but pull your hand away just in time—
(no space) (it $Obj is) on fire, and too hot to touch!
It is, I will admit, a bit less efficient than the previous implementation, and requires a couple more queries. But I believe that cost is more than outweighed by making the whole system less error-prone, and requiring less duplication of code.
What do you all think?
- This is a great idea!
- No, keep it the old way
- I want something else (not the old way or this new proposal)