Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

I7-NL blessings

16 views
Skip to first unread message

steve....@gmail.com

unread,
Aug 22, 2006, 11:54:57 PM8/22/06
to
A recent RAIF discussion between Jason Orendorff and myself considered
a
few examples of the strengths of I7's syntax.

Unfortunately, those examples were all pretty similar: they all
involved
object-loops, where we're essentially trying to produce a collection of
game-objects which satisfied certain conditions. "All the mice in the
library," "the angriest hostile monster near the player," and so on.
I've always felt that "iterating over game objects" was a
straightforward process, and a common one, which unfortunately involved
a lot of typing (in TADS 3), and my discussion with Jason really
emphasized this in my mind. I started thinking that one might adopt the
blessings of I7-NL. (In the case of object-loops, it's a relatively
simple matter of writing a macro and a few support functions.)

I7 was developed from the premise that NL is good at describing the
typical data of IF. Whether or not that's true in abstract, it's
certainly the case that I7-NL is a good syntax for certain programming
constructions, which normally (for me in TADS 3) require significantly
more typing to set down. (TADS 3 is considerably more efficient
overall,
but it is equally certain that I7 is more economical at certain
things.)

So I'm going through the I7 docs again, looking for the blessings of
NL,
and what we can learn (and appropriate) from it. Particularly, looking
with an eye to what is: a) an elegant piece of syntax; and b) not
something that is deeply idiomatic to I7, but which translates directly
into common data structures, such as we find in TADS (such as producing
a complete list of (game-)objects which satisfy a set of constraints).

It's a lengthening list, but here's what I found so far:

-I-

Obviously, the object-loop stuff, mentioned above. Would an object
loop,
which allowed easy condition declaration, encompass the strength of I7
syntax in this area? I think objLoop(Class, condition1, condition2,
...)
is sufficient syntax, but I may be missing something major. (I'm
skipping some minor things, in particular I'm thinking of suprlatives,
but I think that sort of thing is very easily handled by supporting
functions.)

-II-

I think that it's a good technique to simply write all the verbs that
are equivalent, and in what way they're equivalent. ("6.10 Rules
applying to more than one action," ff.) In TADS 3, the action control
is
roughly the same, but you have to specify a separate mapping for each
action/verb (or 'Default'). Perhaps it would be better to allow a list
of actions in the specification, e.g., dobjFor(Smell, Taste). But I
think that would only partly enjoy the simplicity of the I7 syntax.

-III-

In the whitepaper, Graham goes on at length about the superiority of
binary predicates. I must admit, I'm not so sure about this one. As I
understand this lingo, a *unary* predicate is a boolean or numerical
property of an object: candle.lit = true; barbell.weight = 10; etc. A
*binary* predicate is a relationship between objects: candle.location =
startroom; president.loves = [god, power].

I don't think this is new stuff, because it's easy to set up a
so-called
binary predicate. Perhaps the big deal is that I7 provides a better
syntax for this. In any case, I think I can include this in the list,
particularly hoping that others might be able to elaborate on the I7
"binary predicates," either in terms of concept or syntax.

== sorry to cut short. more to come.

vaporware

unread,
Aug 23, 2006, 12:55:03 AM8/23/06
to
steve....@gmail.com wrote:
> So I'm going through the I7 docs again, looking for the blessings of
> NL,
> and what we can learn (and appropriate) from it. Particularly, looking
> with an eye to what is: a) an elegant piece of syntax; and b) not
> something that is deeply idiomatic to I7, but which translates directly
> into common data structures, such as we find in TADS (such as producing
> a complete list of (game-)objects which satisfy a set of constraints).
>
> It's a lengthening list, but here's what I found so far:
>
> -I-
>
> Obviously, the object-loop stuff, mentioned above. Would an object
> loop,
> which allowed easy condition declaration, encompass the strength of I7
> syntax in this area? I think objLoop(Class, condition1, condition2,
> ...)
> is sufficient syntax, but I may be missing something major. (I'm
> skipping some minor things, in particular I'm thinking of suprlatives,
> but I think that sort of thing is very easily handled by supporting
> functions.)

Object specifications in I7 can name the 'temporary' objects found by
the query, as well as expressing conditions between them:

if the noun is owned by a person (called shopkeeper) who manages a
store (called shop) which contains an empty cash register, say "[The
shopkeeper] says, 'Hold it, buddy. You owe [the shop] [price of the
noun] for that.'"

I'm not sure how you'd express that as a series of conditions in TADS
3.

> -III-
>
> In the whitepaper, Graham goes on at length about the superiority of
> binary predicates. I must admit, I'm not so sure about this one. As I
> understand this lingo, a *unary* predicate is a boolean or numerical
> property of an object: candle.lit = true; barbell.weight = 10; etc. A
> *binary* predicate is a relationship between objects: candle.location =
> startroom; president.loves = [god, power].
>
> I don't think this is new stuff, because it's easy to set up a
> so-called
> binary predicate. Perhaps the big deal is that I7 provides a better
> syntax for this. In any case, I think I can include this in the list,
> particularly hoping that others might be able to elaborate on the I7
> "binary predicates," either in terms of concept or syntax.

I thought he was mainly talking about Inform's internals there, not the
benefits of predicates for IF authors...

Anyway, my understanding is: a predicate is a statement which is either
true or false. A unary statement applies to one thing, a binary
statement applies to two things. Using the Prolog syntax, your
predicates would be:

// UNARY
lit(candle).

// BINARY
weighs(barbell, 10).
in(candle, startroom).
loves(president, god).
loves(president, power).

Thus, the I7 condition "if something lit (called X) is in the start
room" translates to the Prologish query:

exists [X]: lit(X) and in(X, startroom).

And the shopkeeper's condition above would be:

exists [Shopkeeper, Shop, R]: is-a(Shopkeeper, person) and
owns(Shopkeeper, noun) and manages(Shopkeeper, Shop) and is-a(Shop,
store) and contains(Shop, R) and is-a(R, cash register) and empty(R).

Now, you could implement the condition as three nested object loops,
trying every possible combination of Shopkeeper/Shop/R until the
condition is satisfied.. but the advantage of putting the query into a
form like this is that you can manipulate and optimize it at compile
time, drastically reducing the number of iterations required.

vw

Graham Nelson

unread,
Aug 23, 2006, 6:14:46 AM8/23/06
to
steve....@gmail.com wrote:
> I don't think this is new stuff, because it's easy to set up a
> so-called
> binary predicate. Perhaps the big deal is that I7 provides a better
> syntax for this.

Partly, I suppose; we don't claim to have invented a never-before-seen
data structure, but we do think that its general usefulness has been
underplayed; it is ubiquitous in natural language, relatively
little-used in programming languages. (But only relatively little.)

> In any case, I think I can include this in the list,
> particularly hoping that others might be able to elaborate on the I7
> "binary predicates," either in terms of concept or syntax.

The real point, I think, is that binary predicates make quantification
much more powerful. Given a unary predicate U (like "open" or
"container"), the repertoire of possible conditions one can ask is
small: open(big metal gate)? or similar, together with quantified
questions such as "is there a D such that open(D)?" or "is every D such
that open(D)"?

Such conditions involve at most one object at a time, and therefore
don't express complicated situations very well. Once you have binary
predicates, you can make conditions of arbitrary arity, though: you
don't just go from one object to two. You can ask if "all open doors
which are in lighted rooms lead to visited rooms", say, which
translates to something like "is every D with open(D) and there exists
an R1 such that room(R1) and in(D, R1) such that there exists an R2
such that leads-to(D, R2) and visited(R2)". This is a condition which
depends in a pretty complicated way on three things at once - the door,
the room it's in, the room it leads to. Binary predicates thus provide
the glue which binds together two objects, and using enough of them you
can bind together any number, in the same way that binary operations in
arithmetic allow for arbitrarily complicated expressions - such as
2*(7+13/5), say, which combines four values to make one new one.

Crucial, in all of this, is that any of the terms of these predicates
can be quantified over: they need not be constants like "the big metal
gate", they can be variables like "something".

_Felix

unread,
Aug 23, 2006, 8:55:19 AM8/23/06
to
Graham Nelson wrote:

> You can ask if "all open doors
> which are in lighted rooms lead to visited rooms", say, which
> translates to something like "is every D with open(D) and there
> exists an R1 such that room(R1) and in(D, R1) such that there
> exists an R2 such that leads-to(D, R2) and visited(R2)".

Is that I6? I don't know how to write in I6, but you seem to have
missed out the part about "lighted". Anyway, some of the ugliness there
is due to not being able to use adjectives to express simple properties
like "open". So I re-wrote it allowing simple adjectives, to make a
fairer comparison:

"is every
open door,
where there exists
a lighted room
and the door is in it,
such that there exists
a visited room such that the door leads to it?"

> Binary predicates thus provide
> the glue which binds together two objects, and using enough of them you
> can bind together any number

I think you can do that in the ugly long-winded format as well.

What I notice is:

where there exists ... and the door is in it

is equivalent to just "in", and:

such that there exists ... such that the door leads to it

is equivalent to just "leads to". So "such that there exists ... such
that ..." is all being silently implied by the grammar of the relation.

It is quite cool, yes.

Neil Cerutti

unread,
Aug 23, 2006, 9:39:52 AM8/23/06
to
On 2006-08-23, steve....@gmail.com <steve....@gmail.com> wrote:
> -I-
>
> Obviously, the object-loop stuff, mentioned above. Would an object loop,
> which allowed easy condition declaration, encompass the strength of I7
> syntax in this area? I think objLoop(Class, condition1, condition2, ...)
> is sufficient syntax, but I may be missing something major. (I'm skipping
> some minor things, in particular I'm thinking of suprlatives, but I think
> that sort of thing is very easily handled by supporting
> functions.)

Python's list comprehensions/generators might be an interesting alternative
to natural language:

A list comprehension:

[ eat(x) for x in objlist if x has edible and nearby(x)]

The above is basically shorthand for the functional:

map(eat, filter(lambda x: x has edible, filter(nearby, objlist)))

(I might be screwing up the lambda syntax)

Or simply gather the objects in a list. The following uses Python's
generators instead, which have a similar syntax:

to_be_eaten = list(x for x in objlist if x has edible and nearby(x))

> -II-
>
> I think that it's a good technique to simply write all the verbs that
> are equivalent, and in what way they're equivalent. ("6.10 Rules
> applying to more than one action," ff.) In TADS 3, the action control
> is
> roughly the same, but you have to specify a separate mapping for each
> action/verb (or 'Default'). Perhaps it would be better to allow a list
> of actions in the specification, e.g., dobjFor(Smell, Taste). But I
> think that would only partly enjoy the simplicity of the I7 syntax.

I'm not sure what you're refering to, here.

> -III-
>
> In the whitepaper, Graham goes on at length about the superiority of
> binary predicates. I must admit, I'm not so sure about this one. As I
> understand this lingo, a *unary* predicate is a boolean or numerical
> property of an object: candle.lit = true; barbell.weight = 10; etc. A
> *binary* predicate is a relationship between objects: candle.location =
> startroom; president.loves = [god, power].

Graham was discussing limitations of model worlds and what you can say
about them.

eat(spinach)

is "unary", as were Scott Adams's early parsers. He points out that you can
emulate binary commands in a unary command structure:

wield(sword)
attack(troglodyte)

Finally, it's arguable that eat(spinach) is arguable binary, but that the
"actor" is assumed to be the player.

He goes on to argue that "trinary" (I can't remember if he used such a
term) commands, e.g., "attach the wing to the plane with the glue" are not
of much use, and so were not added to I7.

> I don't think this is new stuff, because it's easy to set up a so-called
> binary predicate. Perhaps the big deal is that I7 provides a better
> syntax for this. In any case, I think I can include this in the list,
> particularly hoping that others might be able to elaborate on the I7
> "binary predicates," either in terms of concept or syntax.

I need to reread that section, but I don't think it had much to do with NL;
it was modeling the world.

--
Neil Cerutti

Krister Fundin

unread,
Aug 23, 2006, 9:44:14 AM8/23/06
to

<steve....@gmail.com> skrev i meddelandet
news:1156305297.0...@p79g2000cwp.googlegroups.com...

>
> Obviously, the object-loop stuff, mentioned above. Would an object
> loop,
> which allowed easy condition declaration, encompass the strength of I7
> syntax in this area? I think objLoop(Class, condition1, condition2,
> ...)
> is sufficient syntax, but I may be missing something major. (I'm
> skipping some minor things, in particular I'm thinking of suprlatives,
> but I think that sort of thing is very easily handled by supporting
> functions.)
>

Actually, I have been working on something like this for TADS 3.
Basically, what you need is just a function that determines if a given
object meets a certain set of criteria, and then you can build other
functions on top of that. Some examples:

"All the mice in the library", becomes

all(Mouse, &isIn, library);

"Now all the mice in the library are scared", becomes:

forall(cur, Mouse, &isIn, library) cur.makeScared();

"The angriest hostile monster near the player", becomes

most(&angerLevel, Monster, &hostile, &isNear, gPlayerChar);

(You'll have to define what 'isNear' means, of course. In this
case, it could perhaps be replaced with 'canSee'.)

>
> In the whitepaper, Graham goes on at length about the superiority of
> binary predicates. I must admit, I'm not so sure about this one. As I
> understand this lingo, a *unary* predicate is a boolean or numerical
> property of an object: candle.lit = true; barbell.weight = 10; etc. A
> *binary* predicate is a relationship between objects: candle.location =
> startroom; president.loves = [god, power].
>
> I don't think this is new stuff, because it's easy to set up a
> so-called
> binary predicate. Perhaps the big deal is that I7 provides a better
> syntax for this. In any case, I think I can include this in the list,
> particularly hoping that others might be able to elaborate on the I7
> "binary predicates," either in terms of concept or syntax.

Well, a predicate is just a boolean function or a method that you call.
The usefulness comes when you can loop over the parameters in
various ways. Another example from my extension:

"The number of cookies that are not in closed cookie jars", becomes:

count(Cookie, not, &isIn, any, [CookieJar, not, &isOpen]);

It's not quite as good-looking, but it works. It's also possible to have
ternary, quaternary, etc. predicates and loop over them in the same
way, though I doubt that it's of much use.

I haven't added any "(called x)" equivalent yet, but I suppose that
that would be possible as well, using strings to identify matches.

-- Krister Fundin

steve....@gmail.com

unread,
Aug 23, 2006, 1:25:52 PM8/23/06
to
Krister Fundin wrote:

> Actually, I have been working on something like this for TADS 3.\

Cool!

> Basically, what you need is just a function that determines if a given
> object meets a certain set of criteria, and then you can build other
> functions on top of that.

Yes, that was my idea. I've wandered in a slightly different
direction... Let me compare notes a bit here.

> "All the mice in the library", becomes
>
> all(Mouse, &isIn, library);

What do you think of my suggestion that we allow conditionals as
arguments? E.g., "all(Mouse, location == library)"? Or perhaps
"all(Mouse, isIn(library))"?

> "Now all the mice in the library are scared", becomes:
>
> forall(cur, Mouse, &isIn, library) cur.makeScared();

Do we want 'cur' here, or is it just as well to suppress it? E.g.,
"forall(Mouse, ...) { makeScared(); }"

> "The angriest hostile monster near the player", becomes
>
> most(&angerLevel, Monster, &hostile, &isNear, gPlayerChar);

My sense is that "most" would be a separate function: "objLoop(Monster,
hostile, isNear(gPlayerChar)).most(&angerLevel)". I think it makes
things simpler, for us and for the user, to have one main
macro/function (objLoop or so) which produces Collections from a
specification, and then use TADS 3's built-in Collection/List/Vector
methods, since they already provide a powerful set of tools for
manipulating collections, perhaps also expanding these to include such
concepts as "most()" and whatnot.

(Thanks to Eric Eve for emphasizing this in a recent email.)

> "The number of cookies that are not in closed cookie jars", becomes:
>
> count(Cookie, not, &isIn, any, [CookieJar, not, &isOpen]);
>
> It's not quite as good-looking, but it works.

A couple ideas here. First, as above, I think we're better with a
simpler function, then taking the return value's length(). More
importantly, I think even with this simplification the syntax is too
complicated. I think it would be better to have fewer arguments and
perhaps separate phases.

This reminds me of the other main point that Eric made in that recent
email, that because objLoop() returns a Collection, it lends itself to
a hybrid use. Eric's example was:

objLoop(Door, isOpen).subset({x: x.ofKind(Lockable) &&
x.name.find('red')});

or, taking our current example:

objLoop(Cookie).subset({x: x.location.isOpen ||
!x.location.ofKind(CookieJar)}).length()

That's a little more typing, but lot easier for me to read -- but
perhaps that's only because I'm already used to the anonymous-function
syntax. Whichever one we like better at this point, it looks like we
could make further progress with the syntax.

> I haven't added any "(called x)" equivalent yet, but I suppose that
> that would be possible as well, using strings to identify matches.

Yes I think that might be nice. On the other hand, while I'm inspired
by I7, I don't want to be tethered to it. And this doesn't seem as
useful as the rest, since one can (I think normally) easily backtrack
from final value to the intervening local. Borrowing vaporware's
example:

if the noun is owned by a person (called shopkeeper) who manages a
store (called shop) which contains an empty cash register, say "[The
shopkeeper] says, 'Hold it, buddy. You owe [the shop] [price of the
noun] for that.'"

Here we have a couple "locals", namely "shopkeeper" and "shop", but
these locals are pretty easy to construct from the noun at any point
("noun.owner" and "noun.owner.myShop" for instance), and don't really
save much effort by being "local".

Definitely I'm interested in the prospect of maintaining information in
the objLoop (or whatnot) for use after the value is returned, but if it
gets too messy, it might be easier to reconstruct the information from
the return value. (2 caveats: for all I know, there may be a compelling
example where this isn't possible; it's getting complicated enough at
this point that I need to think some more before I feel comfortable.)

steve....@gmail.com

unread,
Aug 23, 2006, 1:42:54 PM8/23/06
to
I clutzed:

> > Basically, what you need is just a function that determines if a given
> > object meets a certain set of criteria, and then you can build other
> > functions on top of that.
>
> Yes, that was my idea.

Ouch! Egad! I meant "that was my idea also" i.e., I was thinking that
way myself. I had no intention of claiming ownership of the idea!
Sorry.

Emily Short

unread,
Aug 23, 2006, 2:02:03 PM8/23/06
to

steve....@gmail.com wrote:
> Here we have a couple "locals", namely "shopkeeper" and "shop", but
> these locals are pretty easy to construct from the noun at any point
> ("noun.owner" and "noun.owner.myShop" for instance), and don't really
> save much effort by being "local".

Just off the top of my head, I can think of a place where I'd really
not want to have to reconstruct -- if the loop itself is complicated or
if I'm doing some bit of tricky pathfinding, I want to minimize the
number of times I have to run these processes, for performance reasons.

steve....@gmail.com

unread,
Aug 23, 2006, 4:22:46 PM8/23/06
to
Emily Short wrote:

> Just off the top of my head, I can think of a place where I'd really

> not want to have to reconstruct [locals] -- if the loop itself is complicated or


> if I'm doing some bit of tricky pathfinding, I want to minimize the
> number of times I have to run these processes, for performance reasons.

Of course, performance is a big concern for users of I7. Plus, if
you're constrained to use I7 syntax, you can't really reconstruct the
locals without going through the same rigmarole (which I think is what
you're saying about the loop itself being complicated). Or in other
words, because there's no continuity of process between I6 and I7, you
can't really reconstruct the local within the lower-level syntax (which
is what I was suggesting in effect). N.B.: regardless of your system,
if you're doing computationally expensive searches, you might want to
arrange the search engine such that it first checks a cache for useful
information, which should be available for as long as it remains
correct.

But this thread is not about I7. We're discussing and experimenting
with shorthand macros/functions, for processes so common that they
arguably deserve their own shorthand. Insofar as it's all about
convenience, you can expect performance to be ranked pretty low, if not
ignored altogether. (Anyhow, we can do things the longer (and standard)
way whenever we're worried about performance; that's one strength of
the incremental approach, the continuity of process.)

vaporware

unread,
Aug 23, 2006, 5:30:17 PM8/23/06
to
steve....@gmail.com wrote:
> if the noun is owned by a person (called shopkeeper) who manages a
> store (called shop) which contains an empty cash register, say "[The
> shopkeeper] says, 'Hold it, buddy. You owe [the shop] [price of the
> noun] for that.'"
>
> Here we have a couple "locals", namely "shopkeeper" and "shop", but
> these locals are pretty easy to construct from the noun at any point
> ("noun.owner" and "noun.owner.myShop" for instance), and don't really
> save much effort by being "local".
>
> Definitely I'm interested in the prospect of maintaining information in
> the objLoop (or whatnot) for use after the value is returned, but if it
> gets too messy, it might be easier to reconstruct the information from
> the return value. (2 caveats: for all I know, there may be a compelling
> example where this isn't possible; it's getting complicated enough at
> this point that I need to think some more before I feel comfortable.)

I suppose naming the local results isn't as important if all your
relations are one-to-one and thus expressible as scalar properties. For
example, here's one use of that feature in I7:

Seeking relates various people to one thing. The verb to seek (...)
implies the seeking relation. Membership relates various people to
various gangs. The verb to be a member of implies the membership
relation.
...
Instead of taking something which is sought by a person (called dude)
who is a member of a violent gang (called crew): say "[The dude] and
his fellow [crew] find you, steal [the noun], and throw you into a
snake pit."; end the game in death.

In this example there's no efficient way to recover "dude" or "crew"
knowing only the noun. Even if the noun has a property containing the
list of people who are seeking it, you'll still have to go through that
list again to find each person, then go through the list of gangs each
person is a member of to test whether it's a violent one.

vw

Krister Fundin

unread,
Aug 23, 2006, 5:35:27 PM8/23/06
to

<steve....@gmail.com> skrev i meddelandet
news:1156353952.3...@m73g2000cwd.googlegroups.com...

> Krister Fundin wrote:
>
>> Basically, what you need is just a function that determines if a given
>> object meets a certain set of criteria, and then you can build other
>> functions on top of that.
>
> Yes, that was my idea. I've wandered in a slightly different
> direction... Let me compare notes a bit here.

Okay. But note that I'm not much for theory. I prefer to just get in there
and create something that works. Perhaps it would be better to upload
this as a beta right away so that it can be tested in practice. I'm pretty
content with the syntax as of now.

>> "All the mice in the library", becomes
>>
>> all(Mouse, &isIn, library);
>
> What do you think of my suggestion that we allow conditionals as
> arguments? E.g., "all(Mouse, location == library)"? Or perhaps
> "all(Mouse, isIn(library))"?

That could perhaps work if 'all' was a macro, but here it really needs
to be a function -- otherwise it wouldn't be possible to do all that
tricky stuff with nested loops and whatnot. However, it's always
possible to use an anonymous function as a criterion:

all(Mouse, { x: x.location == library });

>> "Now all the mice in the library are scared", becomes:
>>
>> forall(cur, Mouse, &isIn, library) cur.makeScared();
>
> Do we want 'cur' here, or is it just as well to suppress it? E.g.,
> "forall(Mouse, ...) { makeScared(); }"

I can't see how that would work out. How can you refer to the
mouse if it doesn't have a name? By the way, the macro is:

#define forall(var, [args]) foreach (local var in all(args...))

>> "The angriest hostile monster near the player", becomes
>>
>> most(&angerLevel, Monster, &hostile, &isNear, gPlayerChar);
>
> My sense is that "most" would be a separate function: "objLoop(Monster,
> hostile, isNear(gPlayerChar)).most(&angerLevel)". I think it makes
> things simpler, for us and for the user, to have one main
> macro/function (objLoop or so) which produces Collections from a
> specification, and then use TADS 3's built-in Collection/List/Vector
> methods, since they already provide a powerful set of tools for
> manipulating collections, perhaps also expanding these to include such
> concepts as "most()" and whatnot.

That doesn't really seem any simpler to me. I guess these methods
would come in handy when you have a list of objects stored
somewhere, but I would still like to keep the functions around,
especially since you can use them as criteria of their own.

>> "The number of cookies that are not in closed cookie jars", becomes:
>>
>> count(Cookie, not, &isIn, any, [CookieJar, not, &isOpen]);
>>
>> It's not quite as good-looking, but it works.
>
> A couple ideas here. First, as above, I think we're better with a
> simpler function, then taking the return value's length().

That is exactly what 'count' does. It calls 'all' and returns the length
of the resulting list. You can still do it by hand, but the idea was to
reduce the syntactic overhead to a bare minimum.

> More
> importantly, I think even with this simplification the syntax is too
> complicated. I think it would be better to have fewer arguments and
> perhaps separate phases.
>
> This reminds me of the other main point that Eric made in that recent
> email, that because objLoop() returns a Collection, it lends itself to
> a hybrid use. Eric's example was:
>
> objLoop(Door, isOpen).subset({x: x.ofKind(Lockable) &&
> x.name.find('red')});

In my notation, just for comparison:

all(Door, &isOpen, Lockable, { x: x.name.find('red') });

> or, taking our current example:
>
> objLoop(Cookie).subset({x: x.location.isOpen ||
> !x.location.ofKind(CookieJar)}).length()
>
> That's a little more typing, but lot easier for me to read -- but
> perhaps that's only because I'm already used to the anonymous-function
> syntax.

We seem to have wildly different ideas about what is simple to read. The
whole point of my extension was to eliminate the need for complicated
constructs like these. I don't see why one should be forced to type twice
as much as what's really needed. Those that prefer could still use such an
approach, but I don't think it would work as well in a more complicated
case. What I like about the criteria is that you can nest and extend them
indefinitely, using the same basic syntax no matter how complex the
situation gets.

>> I haven't added any "(called x)" equivalent yet, but I suppose that
>> that would be possible as well, using strings to identify matches.
>
> Yes I think that might be nice. On the other hand, while I'm inspired
> by I7, I don't want to be tethered to it. And this doesn't seem as
> useful as the rest, since one can (I think normally) easily backtrack
> from final value to the intervening local.

Sure. What's also useful, though, is that you can reuse the match later
on in another criteria. I'd give an example if I could come up with one.
Sort of like in regular expressions where you want to match a string that
begins and ends with the same substring.

-- Krister Fundin

steve....@gmail.com

unread,
Aug 23, 2006, 6:51:05 PM8/23/06
to
Krister Fundin wrote:
> > Let me compare notes a bit here.
>
> Okay. But note that I'm not much for theory. I prefer to just get in there
> and create something that works.

Sure, and solving *a* problem can help uncover the dynamics of related
problems. I'm still trying to figure out the scope of the project,
though I have written some code here also.

> Perhaps it would be better to upload
> this as a beta right away so that it can be tested in practice. I'm pretty
> content with the syntax as of now.

Sure. And the drawing board is a nice place to be, too.

I'll be following up Neil's references too: that stuff looks very
promising.

> >> "All the mice in the library", becomes
> >>
> >> all(Mouse, &isIn, library);
> >
> > What do you think of my suggestion that we allow conditionals as
> > arguments? E.g., "all(Mouse, location == library)"? Or perhaps
> > "all(Mouse, isIn(library))"?
>
> That could perhaps work if 'all' was a macro, but here it really needs
> to be a function -- otherwise it wouldn't be possible to do all that
> tricky stuff with nested loops and whatnot.

Yeah, I got it working using a macro. I see what you mean about
recursively calling the thing, though. Hm.

> However, it's always
> possible to use an anonymous function as a criterion:
>
> all(Mouse, { x: x.location == library });

Sure, that's what the macro expands into anyway.

I like functions better than macros. Maybe the anonymous function for
this isn't so bad. I like the brevity permitted by the macro, though it
looks screwy. I guess the foreclosure of recursion is the main strike
against.

> >> "Now all the mice in the library are scared", becomes:
> >>
> >> forall(cur, Mouse, &isIn, library) cur.makeScared();
> >
> > Do we want 'cur' here, or is it just as well to suppress it? E.g.,
> > "forall(Mouse, ...) { makeScared(); }"
>
> I can't see how that would work out.

I thought I had it, but I was confused. You're right; sorry.

> > My sense is that "most" would be a separate function: "objLoop(Monster,

> > hostile, isNear(gPlayerChar)).most(&angerLevel)". [...]


>
> That doesn't really seem any simpler to me. I guess these methods
> would come in handy when you have a list of objects stored
> somewhere, but I would still like to keep the functions around,
> especially since you can use them as criteria of their own.

Good point.

> > [A]s above, I think we're better with a


> > simpler function, then taking the return value's length().
>
> That is exactly what 'count' does. It calls 'all' and returns the length
> of the resulting list. You can still do it by hand, but the idea was to
> reduce the syntactic overhead to a bare minimum.

Okay. Well my eyes, which are new to your syntax, saw an overloaded
single method which would be more immediately digestible broken-up. I
guess once my eyes get used to your approach, they might like it even
better than the alternatives.

> We seem to have wildly different ideas about what is simple to read.

Somehow I doubt that. I think the designer of a syntax will find it
easier to read than someone who's encountered it for the first time.

> The
> whole point of my extension was to eliminate the need for complicated
> constructs like these. I don't see why one should be forced to type twice
> as much as what's really needed. Those that prefer could still use such an
> approach, but I don't think it would work as well in a more complicated
> case. What I like about the criteria is that you can nest and extend them
> indefinitely, using the same basic syntax no matter how complex the
> situation gets.

Definitely. I still like the possibility of breaking them up, and
combining/hybridizing them using the native Collective methods.

> >> I haven't added any "(called x)" equivalent yet, but I suppose that
> >> that would be possible as well, using strings to identify matches.
> >
> > Yes I think that might be nice. On the other hand, while I'm inspired
> > by I7, I don't want to be tethered to it. And this doesn't seem as
> > useful as the rest, since one can (I think normally) easily backtrack
> > from final value to the intervening local.
>
> Sure. What's also useful, though, is that you can reuse the match later
> on in another criteria.

Yes, if I'm understanding you, that was another of vaporware's main
points, that you can express conditions between the 'temporary' objects
found by the query.

So in the example...

if the noun is owned by a person (called shopkeeper) who manages a
store (called shop) which contains an empty cash register

...reading backwards: the empty cash register is an object generated
from the shop, which is a "temporary" object (or what I have called
"local" object), and which was generated from the shopkeeper, another
"temporary" object.

It would be interesting to find a nice algorithm for doing this kind of
search.

na...@natecull.org

unread,
Aug 24, 2006, 1:01:48 AM8/24/06
to

steve....@gmail.com wrote:
> Obviously, the object-loop stuff, mentioned above. Would an object
> loop,
> which allowed easy condition declaration, encompass the strength of I7
> syntax in this area? I think objLoop(Class, condition1, condition2,
> ...)
> is sufficient syntax, but I may be missing something major.

I think the main thing is that you want object specifications, not
object loops per se, treated as a first-class object.

I like the Prolog-like formulation of them myself, since that gives you
the pattern matching and substitution of variables which you can then
use later in your code. Not quite sure how you'd do that in C++ like
code.


> -III-
>
> In the whitepaper, Graham goes on at length about the superiority of
> binary predicates. I must admit, I'm not so sure about this one.

As Graham and others described later downthread, binary predicates are
what I was ranting about earlier and called 'relations'. They make
great glue.

president.loves = [god, power]

is not a true formulation of the binary predicate

loves(president, god)
loves(president, power)

- it only shows half of the relationship. IE, for every 'loves' there's
a matching 'loved-by':

god.loved-by = [president]
power.loved-by = [president]

and each end of these need to be automatically updated when you change
the other. You could fake this with a sufficiently smart OO system that
allowed calculated properties that looked and felt exactly like
ordinary slots, with setter/getter methods - .NET's properties could
probably do this. And then each object would need to store the
predicate information not in itself but in a separate predicate object.

Mike Roberts

unread,
Aug 24, 2006, 1:36:49 AM8/24/06
to
<na...@natecull.org> wrote:
> president.loves = [god, power]
>
> is not a true formulation of the binary predicate
>
> loves(president, god)
> loves(president, power)
>
> - it only shows half of the relationship. IE, for every 'loves' there's
> a matching 'loved-by':
>
> god.loved-by = [president]
> power.loved-by = [president]
>
> and each end of these need to be automatically updated when you
> change the other. You could fake this with a sufficiently smart OO
> system that allowed calculated properties [...]

I think the right OO formulation actually wouldn't involve a lot of
properties with two-way linkages and so on, but rather would be something a
bit simpler, like this:

<anonymous object> Loves(president, god);
<anonymous object> Loves(president, power);

In other words, a class called Loves that represents this particular binary
relationship, and an instance of the class for each relationship you want to
represent. A few methods would be needed to establish and break
relationships and perform searches (all relatively straightforward, I
think). For example, 'Who loves X?' is obtained from the subset of Loves
instances for which the second operand equals X. I think these would
largely get pushed up into a couple of Relation base classes, so creating a
relation boils down to something trivial -

class Loves: ManyToMany;

and then dynamic operations on these look like method calls on the
relationship class:

Loves.add(president, power);
Loves.remove(president, congress);
lst = Loves.allTo(power); // who loves power?
lst = Loves.allFrom(president); // what does president love?

Combination questions can be answered by adding ("OR" combinations) or
intersecting ("AND" combinations) result lists - not as efficient as prolog
could do with implied nested loops, but maybe not too bad; and with a little
more thinking, there might even be good ways of handling compound questions
efficiently with closures (anonymous functions), for example.

--Mike
mjr underscore at hotmail dot com


Graham Nelson

unread,
Aug 24, 2006, 5:06:31 AM8/24/06
to
steve....@gmail.com wrote:
> N.B.: regardless of your system,
> if you're doing computationally expensive searches, you might want to
> arrange the search engine such that it first checks a cache for useful
> information, which should be available for as long as it remains
> correct.

You are not the first person to think of this strategy. The
path-finding algorithm, which is the most expensive in I7's run-time
support, does indeed cache.

steve....@gmail.com

unread,
Aug 24, 2006, 7:41:17 AM8/24/06
to

Graham Nelson wrote:
> steve....@gmail.com wrote:
> > N.B.: regardless of your system,
> > if you're doing computationally expensive searches, you might want to
> > arrange the search engine such that it first checks a cache for useful
> > information, which should be available for as long as it remains
> > correct.
>
> You are not the first person to think of this strategy.

You mean in the context of I7 optimizations? I certainly hope not! (Or
in the context of remarking this technique to readers of RAIF,
particularly those who are overly anxious about performance? I would
guess it's been suggested many times before.)

> The
> path-finding algorithm, which is the most expensive in I7's run-time
> support, does indeed cache.

Glad to hear it. Keep up the good work!

steve....@gmail.com

unread,
Aug 24, 2006, 8:16:56 AM8/24/06
to
vaporware wrote:
> I suppose naming the local results isn't as important if all your
> relations are one-to-one and thus expressible as scalar properties. For
> example, here's one use of that feature in I7:

[snipped example]

Thanks for the example -- it clearly shows the limitation of my initial
idea.

> In this example there's no efficient way to recover "dude" or "crew"
> knowing only the noun. Even if the noun has a property containing the
> list of people who are seeking it, you'll still have to go through that
> list again to find each person, then go through the list of gangs each
> person is a member of to test whether it's a violent one.

Yes, so unless you keep track of the path, you have to do, in effect,
the same search twice, if you want to reconstruct the temporary objects
discovered along the way.

It seems increasingly like a robust search problem, as we move in this
direction. I had been thinking of this as a syntax for expressing what
is already a common search in TADS 3: object iteration with conditional
checking. That would probably be a good thing too, but it suggests a
more complicated search, such as the recursive setup that Krister is
working on.

Krister Fundin

unread,
Aug 24, 2006, 8:41:41 AM8/24/06
to

<steve....@gmail.com> skrev i meddelandet
news:1156373465.1...@75g2000cwc.googlegroups.com...

> Krister Fundin wrote:
>
>> We seem to have wildly different ideas about what is simple to read.
>
> Somehow I doubt that. I think the designer of a syntax will find it
> easier to read than someone who's encountered it for the first time.

True, but I also get the impression that you don't like the secret magic
black box kind of functions, while I prefer to hide away as much of the
dirty-work as possible.

>> The
>> whole point of my extension was to eliminate the need for complicated
>> constructs like these. I don't see why one should be forced to type twice
>> as much as what's really needed. Those that prefer could still use such
>> an
>> approach, but I don't think it would work as well in a more complicated
>> case. What I like about the criteria is that you can nest and extend them
>> indefinitely, using the same basic syntax no matter how complex the
>> situation gets.
>
> Definitely. I still like the possibility of breaking them up, and
> combining/hybridizing them using the native Collective methods.

Perhaps the cookie example was too simple. Let's try a more complicated
scenario:

"repeat with cur running through every thief that can see a treasure which
is not carried by anyone", becomes:

forall(cur, Thief, &canSee, any, [Treasure, not, &isIn, any, [Person]]) { }

This actually expands to three nested object loops, and is pretty hard to
emulate using 'subset' and such methods. The single function approach
scales more linearly, so to speak.

>> >> I haven't added any "(called x)" equivalent yet, but I suppose that
>> >> that would be possible as well, using strings to identify matches.
>

> Yes, if I'm understanding you, that was another of vaporware's main
> points, that you can express conditions between the 'temporary' objects
> found by the query.
>
> So in the example...
>
> if the noun is owned by a person (called shopkeeper) who manages a
> store (called shop) which contains an empty cash register

Just a note here. The above expression doesn't need any "(called x)" in
order to be properly evaluated. It would look something like this:

if (objMeets(gDobj, &isOwnedBy, any, [Person, &manages, any,
[Store, &contains, any, [CashRegister, &isEmpty]]]))

I was rather thinking of something like:

"if the player has a friend (called joe) and another friend who is an
enemy of joe, ..."

But that wouldn't work neither in I7 nor my extension, and I guess that it
would be better to express it as:

"if the player has a friend who has an enemy who is a friend of the
player, ..."

Anyway, I'll try to finish my code and write some docs for it.

-- Krister Fundin

steve....@gmail.com

unread,
Aug 24, 2006, 9:18:50 AM8/24/06
to
Krister Fundin wrote:

> "repeat with cur running through every thief that can see a treasure which
> is not carried by anyone", becomes:
>
> forall(cur, Thief, &canSee, any, [Treasure, not, &isIn, any, [Person]]) { }

I'm beginning to really appreciate your syntax, and I think I've made
some progress in understanding what else might be wanted here.

My first reaction is to nest things differently, "break it up into
phases" as I've been suggesting. Something vaguely like this:

forall(Thief t, canSee, Treasure treas) {
objLoop(treas, !location.ofKind(Person)) {}
}

This "vertical nesting" has the benefit not only of breaking the
process into simple conceptual steps, but also of allowing us to access
the locals, or to place any other code, at any point within the
process. It would also enable us to handle such problems as:

> "if the player has a friend (called joe) and another friend who is an
> enemy of joe, ..."

forall(person p, isFriend, gPlayerChar) {
forall(person otherp, isFriend(gPlayerChar), !isFriend(p)) {}
}

Although when it comes to that, I'd probably prefer the more
brute-force...

foreach(local p in gPlayerChar.friendList)
foreach(local q in gPlayerChar.friendList)
if(p.enemyList.indexOf(q)) {}

...if only because it's very simple and straightforward.

> Anyway, I'll try to finish my code and write some docs for it.

Great! I'm really looking forward to it.

Kevin Forchione

unread,
Aug 24, 2006, 12:33:07 PM8/24/06
to
"Mike Roberts" <mj...@hotmail.com> wrote in message
news:RVaHg.3425$q63....@newssvr13.news.prodigy.com...

Kevin Forchione

unread,
Aug 24, 2006, 12:56:12 PM8/24/06
to
"Kevin Forchione" <ke...@lysseus.com> wrote in message
news:2xkHg.2801$y61.2006@fed1read05...

> "Mike Roberts" <mj...@hotmail.com> wrote in message
> news:RVaHg.3425$q63....@newssvr13.news.prodigy.com...
<snip>

>> Combination questions can be answered by adding ("OR" combinations) or
>> intersecting ("AND" combinations) result lists - not as efficient as
>> prolog could do with implied nested loops, but maybe not too bad; and
>> with a little more thinking, there might even be good ways of handling
>> compound questions efficiently with closures (anonymous functions), for
>> example.

Sounds viable to me. You could probably make the AND and OR relationships
objects as well, so that you're just working within a more generalized
object framework for all searches. Macros could be used to make the syntax
more palatable, if necessary.

--Kevin

Kevin Forchione

unread,
Aug 24, 2006, 1:20:53 PM8/24/06
to
"Mike Roberts" <mj...@hotmail.com> wrote in message
news:RVaHg.3425$q63....@newssvr13.news.prodigy.com...
> <na...@natecull.org> wrote:
>> president.loves = [god, power]
>>
>> is not a true formulation of the binary predicate
>>
>> loves(president, god)
>> loves(president, power)
>>
>> - it only shows half of the relationship. IE, for every 'loves' there's
>> a matching 'loved-by':
>>
>> god.loved-by = [president]
>> power.loved-by = [president]
>>
>> and each end of these need to be automatically updated when you
>> change the other. You could fake this with a sufficiently smart OO
>> system that allowed calculated properties [...]
>
> I think the right OO formulation actually wouldn't involve a lot of
> properties with two-way linkages and so on, but rather would be something
> a bit simpler, like this:
>
> <anonymous object> Loves(president, god);
> <anonymous object> Loves(president, power);
>
> In other words, a class called Loves that represents this particular
> binary relationship, and an instance of the class for each relationship
> you want to represent.

One of the issues that comes up, of course, is that of equivalence: [A,B] ==
[B,A] For instance, Loves(bob, sue) could be interpreted to mean "bob loves
sue", which isn't the same as Loves(sue, bob) and indeed this relationship
might not exist in our game.

Then we are left the issue of element order when doing searches. In a query
such as Loves.allTo() we're making the assumption that allTo() represents a
search of the 2nd list element of the relationship for each Loves object,
while allFrom() represents a search of the 1st. In a n-ary relationship then
it would be necessary to have a more generalized method to indicate which
slot in the relationship we are searching for: all(idx, val) perhaps.

--Kevin

steve....@gmail.com

unread,
Aug 24, 2006, 5:40:57 PM8/24/06
to
Krister Fundin wrote:
> >> forall(cur, Mouse, &isIn, library) cur.makeScared();
> >
> > Do we want 'cur' here, or is it just as well to suppress it? E.g.,
> > "forall(Mouse, ...) { makeScared(); }"
>
> I can't see how that would work out. How can you refer to the
> mouse if it doesn't have a name?

Well, there is one wiggle: inside the forall() block, 'Mouse' could be
reassigned to be that local variable. Not ideal, but it would work.
Then one could write:

forall(Mouse, ...) { /* here Mouse is the class, as usual */
Mouse.makeScared(); /* here Mouse is an object ofKind(Mouse) */
}

steve....@gmail.com

unread,
Aug 24, 2006, 10:46:45 PM8/24/06
to
Kevin Forchione wrote:

> One of the issues that comes up, of course, is that of equivalence: [A,B] ==
> [B,A] For instance, Loves(bob, sue) could be interpreted to mean "bob loves
> sue", which isn't the same as Loves(sue, bob) and indeed this relationship
> might not exist in our game.

I don't see that as a problem at all. Anyone who is working with the
system knows that (a,b) and (b,a) are not the same, and will easily
learn which element is the "subject" (and if their native tongue is
English and the first element is the "subject," it will seem very
natural.) N.B.: in relational logic, it doesn't matter which term is
the subject, so long as the user is consistent.

> Then we are left the issue of element order when doing searches. In a query
> such as Loves.allTo() we're making the assumption that allTo() represents a
> search of the 2nd list element of the relationship for each Loves object,
> while allFrom() represents a search of the 1st.

It's not so much an assumption as it is the meaning of the method. Any
competent user of TADS 3 isn't going to have a problem learning the
meaning of two methods.

> In a n-ary relationship[...]

Is there anything gained from n-ary relationships? If not, it might not
be worth worrying the problematics of it.

na...@natecull.org

unread,
Aug 25, 2006, 1:04:26 AM8/25/06
to

steve....@gmail.com wrote:
> Is there anything gained from n-ary relationships? If not, it might not
> be worth worrying the problematics of it.

One case where 3-term predicates might be useful is exits.

exit(room_from, direction, room_to)

since you want 'direction' to be the identifier of a direction
noun/object which you might also want to refer to as an entity in its
own right.

A third term could also be useful to add a quantifier to a
relationship:

loves(president, god, moderately)
loves(president, power, strongly)

JDC

unread,
Aug 25, 2006, 1:38:49 AM8/25/06
to

na...@natecull.org wrote:
> One case where 3-term predicates might be useful is exits.
>
> exit(room_from, direction, room_to)
>
> since you want 'direction' to be the identifier of a direction
> noun/object which you might also want to refer to as an entity in its
> own right.

One place I found where it might be useful is in conversation
topics:"Love suggests Marriage to Bob." could be

suggests(love_topic, marriage_topic, bob)

to indicate that after discussing love with Bob the game could suggest
discussing marriage.

Jason Orendorff

unread,
Aug 25, 2006, 9:48:17 AM8/25/06
to
steve....@gmail.com wrote:
> Unfortunately, those examples were all pretty similar: they all
> involved object-loops,

There's a PL feature called "pattern matching", which mainstream
languages don't have. Consider Java. Each parameter of a method has a
type and a name. The type restricts; the name binds information (from
the caller, so the method body can access it).

In other PLs, parameters are more or less important. In Ruby,
parameters are less: you don't really have the "restrict" aspect, just
name-binding. In languages with pattern matching, parameters are more:
they can restrict in more ways, and they can bind multiple names.
Patterns also tend to have a syntax that mirrors the syntax of
expressions.

Here's an I7 example where the parameter is restricted:

After printing the name of a person who has tasted the honeydew: ...

You've already seen how (called X) is used to bind names. In I7, noun
phrases implement pattern matching.

This is particularly important in I7 because of rules, but pattern
matching is a cool feature independent of whether you like rules. ML
and Haskell have pattern matching but not rules.

I think they also both have "guards", which are an extension of pattern
matching, and which I7 implements using the keyword "when":

After offering a poisoned thing (called the item) to someone (called
the victim) when the victim has seen the item: ...


> -II-
>
> I think that it's a good technique to simply write all the verbs that
> are equivalent, and in what way they're equivalent.

Actually I7 isn't very good at this... For example, you can say

Instead of kicking, biting, hitting, scratching, or
head-butting a mime: ...

But some things you just can't combine:

Instead of burning a mime or inserting a mime into
something hot: ...

This is something I actually *do* consider a minor convenience thing.
:)


More in a moment.

[j]

Andrew Plotkin

unread,
Aug 25, 2006, 9:51:49 AM8/25/06
to
And also the knowledge relations that always come up in NPC planning:
"Alice knows Bob knows about the time bomb." Although that can go
arbitrarily deep, of course, so it winds up looking more like set
notation than like a list of predicate instances.

--Z

--
"And Aholibamah bare Jeush, and Jaalam, and Korah: these were the borogoves..."
*
It used to be that "conservatives" were in favor of smaller government,
fiscal responsibility, and tighter constraints on the Man's ability to
monitor you, arrest you, and control your life.

Jason Orendorff

unread,
Aug 25, 2006, 10:02:28 AM8/25/06
to
steve....@gmail.com wrote:
> -III-
>
> In the whitepaper, Graham goes on at length about the superiority of
> binary predicates. I must admit, I'm not so sure about this one. As I
> understand this lingo, a *unary* predicate is a boolean or numerical
> property of an object: candle.lit = true; barbell.weight = 10; etc. A
> *binary* predicate is a relationship between objects: candle.location =
> startroom; president.loves = [god, power].
>
> I don't think this is new stuff, because it's easy to set up a
> so-called binary predicate.

My experience is limited, but I'll give this a shot.

There are five flavors of binary relations in I7 (listed here with
rough OOP translations):
calculated ("R relates X to Y when <condition>")
==> a boolean function with two arguments
one-to-many ("R relates various X to one Y")
==> an attribute--or a hashtable if X is something like "numbers"
many-to-many ("...to various Y")
==> a list attribute or hashtable of lists...sort of a pain
symmetric many-to-many ("...to each other")
==> a bigger pain
equivalence ("...to each other in groups")
==> a still bigger pain

All these can be queried going either direction, which I'm finding to
be surprisingly nice in practice. An attribute or hashtable is really
designed for one-way use. For example, containment is a one-to-many
relation; if you implemented containment as a "container" attribute,
you could very easily find the parent of an object. But how do you
find all the contents of an object? You have to either search every
object in the universe (a "full table scan" in database jargon) or go
to a more sophisticated data structure: a "container" attribute
(pointing upwards) plus a "contents" list attribute (pointing
downwards), which must be kept in sync.

This is a case of affordances mattering. Conceptually, two-way links
are no more than one-way links. But it's easy to do one-way links in
OOP, harder to do two-way links; so we write programs with a lot of
one-way links. Are two-way links worth building into a language? I
don't know. (My experience is too limited. Prolog has two-way, or
rather n-way, relations; but Prolog has other problems.)

In I7, you specify the kind of relation you want; I7 picks the data
structures and keeps them consistent. Different affordances. And it
fits in very nicely with I7's syntax and other features: properties
make sentences of the form "Noun is Adj" and can be used in noun
phrases as "Adj..."; relations make sentences of the form "Noun Verb
Noun" and can be used in noun phrases as "...which/that/who Verb Noun"
or "...which/that/whom Noun Verb".

In other words, there's some nice synergy between I7's major features.

Adding relations to an OO language (at the library level) might be
easy; finding this level of synergy--well, that's an art.

[j]

Graham Nelson

unread,
Aug 25, 2006, 12:28:08 PM8/25/06
to
Jason Orendorff wrote:
> Actually I7 isn't very good at this... For example, you can say
>
> Instead of kicking, biting, hitting, scratching, or
> head-butting a mime: ...
>
> But some things you just can't combine:
>
> Instead of burning a mime or inserting a mime into
> something hot: ...
>
> This is something I actually *do* consider a minor convenience thing.

This is why I7 allows actions to be categorised by name:

Burning a mime is anti-mime behaviour. Inserting a mime into something
hot is anti-mime behaviour. Instead of anti-mime behaviour: ...

(This isn't all that appealing for only two cases, but usually there
are more.)

steve....@gmail.com

unread,
Aug 26, 2006, 10:55:05 AM8/26/06
to
Thanks Jason. I must admit that I'm having some trouble understanding
some of the finer details, and at other points I think I'm getting the
point but I'm not positive, so let me ask some questions.

Jason Orendorff wrote:
> There's a PL feature called "pattern matching", which mainstream
> languages don't have. Consider Java. Each parameter of a method has a
> type and a name. The type restricts; the name binds information (from
> the caller, so the method body can access it).
>
> In other PLs, parameters are more or less important. In Ruby,
> parameters are less: you don't really have the "restrict" aspect, just
> name-binding.

By 'parameters' here, do you mean type declarations? (Surely the
data(-handles) themselves, which we pass as arguments, are equally
indespensible whatever the language.)

Of course it's possible to confuse "typed-ness" with "the ability (or
requirement) to declare variable types." Restrictions, generally
speaking, come from the capability or incapability of the language to
(automatically) combine or transform datatypes.

> In languages with pattern matching, parameters are more:
> they can restrict in more ways, and they can bind multiple names.

What does that mean, "bind multiple names"?

> Patterns also tend to have a syntax that mirrors the syntax of
> expressions.
>
> Here's an I7 example where the parameter is restricted:
>
> After printing the name of a person who has tasted the honeydew: ...

Meaning that the parameter is 'person' and the restriction is that
'person.hasTasted.indexOf(honeydew) == true' (or something analogous)?

> You've already seen how (called X) is used to bind names. In I7, noun
> phrases implement pattern matching.

I think you're referring to the fact that in I7 objects don't have
strict handles (internal programmatic object names like startRoom or
'blue_bottle_1'), but names which get parsed?

> This is particularly important in I7 because of rules,

I don't see the intersection there, or any substantial difference
between "before taking the white napkin..." and, say,
"whiteNapkin.before(Take) {...".

> but pattern
> matching is a cool feature independent of whether you like rules. ML
> and Haskell have pattern matching but not rules.

Is there any big difference between intrinsic pattern matching and
programmatic pattern matching? I mean, itsn't it just as well to write
a function capable of matching 'white napkin' with whiteNapkin
(matching it against whiteNapkin.vocabWords or so)?

> I think they also both have "guards", which are an extension of pattern
> matching, and which I7 implements using the keyword "when":
>
> After offering a poisoned thing (called the item) to someone (called
> the victim) when the victim has seen the item: ...

I don't see the difference between restrictions and guards:
restriction: 'a person *who has tasted the honeydew*'; guard:
'someone...*when the victim has seen the item*'.

0 new messages