Path: utzoo!utgpu!attcan!uunet!husc6!purdue!haven!mimsy!chris
From: chris@mimsy.UUCP (Chris Torek)
Newsgroups: comp.lang.modula2
Subject: Re: C v.s. Modula II
Message-ID: <13019@mimsy.UUCP>
Date: 16 Aug 88 07:32:11 GMT
References: <79500004@p.cs.uiuc.edu> <79500010@p.cs.uiuc.edu>
Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742
Lines: 127

In article <79500010@p.cs.uiuc.edu> gillies@p.cs.uiuc.edu writes:
>I will elaborate, since I think parts of my posting were
>misunderstood:

(Some were; some I answered just a few minutes ago)

>Often, for(;;) loops are written with *no body*, and adding additional
>commands can require major restructuring of the loop.
>/* Here is a way to copy a string */
>for(p=charbuf, q=copybuff; *p++=*q++;);
>
>/* What if I wanted to debug this, and print characters as they were copied? */
>for(p=charbuf, q=copybuff; *q;) {
>	putchar(*p++=*q++);		/*  */
>}

Well, you *could* write

	for (p = charbuf, q = copybuf; putchar(*p++ = *q++););

but all of these examples are grotesqueries.

>Notice how I had to restructure the looping condition to make this
>change.  Things can be much worse with a more complicated loop.

I will agree that if you are going to revise a loop you must look
at the `for' part as well as the body.  So what?  If I am going to
revise a

	# unspecified generic odd-looking fully-bracketed language:
	for i from 31 to 97 do a[i] <- f(i); rof;

into a

	for i in a[] do a[i] <- f(i); rof;

I will have to look at the `for' part as well as think about what
is going on.

Certainly C's for statement can be abused, but I think the flexibility
it provides is worth it---look at the number of syntaxes for the Mesa
FOR statement to see what I mean.

>... callback procedures ... How can I express something like this
>in C?

>dict: Dictionary.Handle; 		-- global variable
>
>PrintDictionary: PROCEDURE = {
>  wh: Window.Handle := Somewhere.GetAWindowHandle[];     -- used in callback
>  -- A CallbackProc is a PROC[a:LONG STRING] RETURNS[BOOLEAN];
>  ShowName: Dictionary.CallbackProc = {  
>    Put.String[wh, a];
>    Put.CR[wh];
>    RETURN[TRUE]
>  }
>  Dictionary.Enumerate[dict, ShowName];
>}
>
>I call the Dictionary.Enumerate[] procedure, and it repeatedly makes
>call to the ShowName procedure. FURTHERMORE, from within the ShowName
>procedure, I can access & change the variables global to
>PrintDictionary (wh, the window handle).  This is crucial if you want
>to get something done, in a type-safe manner.  I don't think C++'s
>pointer parameters cut the mustard.....

Yes they do, but I will write this in C, rather than in C++.  (C++ can
make short work of it by using classes, so that is no fun at all.  It
is interesting, though, to note that either Dictionary or Window must
be a subclass of the other to do it.  That problem vanishes with
multiple inheritance, coming soon to a C++ compiler near you :-) .)

	/* typedef int boolean; */ /* in some header; or just use int */

	Dictionary_Handle dict;		/* global, as before */

	struct communique {
		Window_Handle wh;
		/*
		 * any other variables local to PrintDictionary
		 * that might be needed/modified in ShowName
		 * go here too.
		 */
	};

	static boolean ShowName(void *private_data, char *a) {
		struct communique *vars = private_data;

		Put_String(vars->wh, a);
		Put_CR(vars->wh);
		return TRUE;
	}

	void PrintDictionary() {
		struct communique vars;

		vars.wh = Somewhere_GetAWindowHandle();
		Dicationary_Enumerate((void *)&vars, dict, ShowName);
	}

>Callback procedures are a nice way of supporting abstract enumeration
>when the language lacks something like CLU's explicit enumerators.

Yes, they are.  While the above lacks the elegance of Mesa's syntax, it
works; it is portable; it can do everything the Mesa version can do.
Moreover, it does not require displays.  (One can simplify the above
example, eliminating the structure entirely, since there is only a
single pointer variable communicated.  In the general case, though, the
structure is required.)

Personally, I would rather use the elegant syntax.

Note that uplevel variable accesses can always be transformed into
references via an opaque pointer, as I did it above, at no cost to
those routines that do not participate.  The compiler must be able to
tell which procedures have such references, which which procedures call
such procedures, which procedures call procedures that . . ., etc.  A
better method, which does not require recursive inference, is to
generate new functions on the fly that automatically provide the
appropriate hidden argument.  This can be done with some cooperation
from the hardware and the O/S.  David Chase recently stirred up a fuss
in comp.lang.c by mentioning this technique.  (Search for the subject
`partial application', but be forewarned: it quickly evolved into a
debate about self-modifying code.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris