[m-dev.] Fw: Replacement syntax for DCGs

Peter Schachte schachte at cs.mu.OZ.AU
Sun Dec 2 20:13:06 AEDT 2001

On Sat, Dec 01, 2001 at 02:33:23PM +1100, Ralph Becket wrote:
> So the name
> the caller uses to name a thread and the name used by the callee do not
> have to match (and either the caller or the callee may elect not to use
> the _!_ notation, just as with DCGs).

Do you have a reason for not requiring them to match?

Pros for requiring them to match:

  1.	Makes the code easier to understand (for the same reason it's a good idea
	to use the same variable names for the same things in different predicates).

  2.	Better error checking (if someone uses different names, it's not unlikely
	they're calling the wrong predicate or passing the wrong thing).  Think of
	it like type checking, but tighter.  Eg, you may pass many integers to a
	predicate, but you'll have much fewer called "max_depth".

  3.	It makes it easy to treat the threads passing through a predicate as a
	set:  just sort them by name.  Then the caller and callee know how to order
	them.  This makes the whole thing much less error prone, which I see as half
	the reason for having a feature like this.

> 1. stick to the original proposal, adopting a kind of OO-style reading
> (e.g. taking `io ! print(...)' to mean something like "invoke the
> print(...) method on the `io' state thread").

I don't think that reading extends very well to multiple threads.

> 	Pros:
> 	- using a prefix makes it immediately obvious that the call is
> 	  `stateful';
> 	- the arguments indicated by the prefix are appended in the same
> 	  order as they appear, so `a ! b ! p(X)' will expand to
> 	  `p(X, A0, A1, B0, B1)'.
> 	Cons:
> 	- since the arguments are appended, it's slightly odd that the
> 	  thread names appear as prefixes.

Much more importantly to me:

	- It puts the emphasis on the data, rather than the relation.  Since
	  threaded data is often really beside the point (eg, passing an
	  io__state pair to a predicate just so it can print out error
	  messages), that 's the wrong thing to emphasize.

	- With different arguments having different (or no) thread arguments,
	  the code will get ugly and hard to read.  Eg:

	io ! depth ! do_something([]).
	io ! depth ! do_something([foo(X)|Tail]) :-
		io ! io__write(...),
		depth ! increment,
		extract_info(X, Y),
		Y > 7,
		io ! depth ! do_something(Tail).

	  This can be made readable, but weird, by aligning the goals:

	io ! depth ! do_something([]).
	io ! depth ! do_something([foo(X)|Tail]) :-
		io 	   ! io__write(...),
		depth	   ! increment,
			     extract_info(X, Y),
			     Y > 7,
		io ! depth ! do_something(Tail).

On second thought, that's not much more readable, and it's a pain in the ***.  Compare that to:

	do_something([]) ! io ! depth.
	do_something([foo(X)|Tail]) ! io ! depth :-
		io__write(...) ! io,
		increment ! depth,
		extract_info(X, Y),
		Y > 7,
		do_something(Tail) ! io ! depth.

> 3. Include the thread names in the argument list, using ! as a prefix
> operator:
> 	write_list([]           , !io) :- nl(!io).
> 	write_list([X]          , !io) :- print(X, !io).
> 	write_list([X1, X2 | Xs], !io) :-
> 		print(X1, !io),
> 		write_list([Xs | Xs], !io).

Hmmm.  Not bad.  That suggests another alternative:  extend the named argument
syntax for terms to work for predicates and functions, too.  I'll have to
think about that.

> then my feeling is that DCGs et al. are used when it's easier to think
> about a piece of code in partially imperative terms.

I think of it as using the lexical order of goals to convey part of the
meaning of a clause.

> As far as definition by translation goes, that's just the way I think of
> DCGs, rather than w.r.t. some meta-interpreter.  I think insisting on
> having a simple meta-interpreter model is less important than having a
> clear mental picture of how code using the new syntax would work.

I find it important to have a way to think about each part of a clause.  I'd
like to think of

	foo(X) ! y

as meaning something like "execute foo(X) transforming y."

> I'd rather not adopt the set-of-threads philosophy, since it would make
> the transformation more complex, thereby making higher order programming
> with this notation much harder.

As I pointed out above, if it is required to use the same name in call and
clause head, you can just sort them to get the order right.

> Personally I'd rather just have one symbol (!) for state threading.  And
> my loathing for Perl is such that I can't stand the idea of using $array (!)

Syntactic parsimony is a worthy ambition, but I still think we could have a
functional version of value access.  Maybe !!depth to get the current value of
depth?  I think != looks too much like not equal.  !:= for assignment is OK.

I also agree with Zoltan that it's important to have read-only threads (only
one argument added to the predicate).  Occasionally one will want a write-only
thread, too.

Peter Schachte              After we've fed the children and cleaned up the
schachte at cs.mu.OZ.AU        river, let's sit down and debate left vs. right.
www.cs.mu.oz.au/~schachte/      -- Joe Ferguson 
Phone: +61 3 8344 9166      
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au

More information about the developers mailing list