gillies@p.cs.uiuc.edu (08/12/88)
When I first came to grad school, and began writing C code again, I was unexpectedly disappointed. I had grown used to a Modula-II like language (MESA). Some of the things that really bothered me about C were: 1. It's a one-pass-compiler language. So you must pollute your code with multiple declarations if you make forward procedure calls. 2. Local changes can require non-local code restructuring. This is a collosal pain. For instance, moving a procedure in a file often requires adding new procedure declarations. Adding an else-clause can require you to restructure the {}'s. Often, for(;;) loops are written with *no body*, and adding additional commands can require major restructuring of the loop. 3. Include files are hard to keep track of, nest, and structure in a big project. Modules & Interfaces seem to work much better. 4. I really miss the way MESA treats records as first class types. For instance, you could say: Gillies: RECORD[first: INTEGER, last: CARDINAL, data: CHAR]; var: Gillies <- [-5, 23, 'c]; -- (record assignment) var1: Gillies <- [first: -5, data: 'c, last: 23];-- (using named fields) 5. I miss MESA INLINE procedures. They are simpler and (often) more efficient than C's macro expansions. 6. C has no provision for callback procedures (you need nested procedures and the right semantics for the 'display'). So data abstraction is hindered in C. Don Gillies, Dept. of Computer Science, University of Illinois 1304 W. Springfield, Urbana, Ill 61801 ARPA: gillies@cs.uiuc.edu UUCP: {uunet,ihnp4,harvard}!uiucdcs!gillies
chris@mimsy.UUCP (Chris Torek) (08/14/88)
In article <79500004@p.cs.uiuc.edu> gillies@p.cs.uiuc.edu writes: >... had grown used to a Modula-II like language (MESA). Mesa is considerably more useful for writing Real Programs than is Modula-II, if only because its I/O really is standard (or at least only has to deal with one O/S!). So this is more of a Mesa-vs-C: >Some of the things that really bothered me about C were: > >1. It's a one-pass-compiler language. So you must pollute your >code with multiple declarations if you make forward procedure calls. As a matter of taste, I happen to like a requirement for forward declarations that must match the later definitions. (I have an intense *dis*like for Pascal's forward, which requires omitting parameters in the actual definition.) Forward declarations help me, as a reader, know what types are attached to functions and arguments. (Of course, this is less important with better programming environments, where I can put the definition up in another window at the touch of a few keys. On the other hand, I am now at home, typing away on my H19, and there is no other window....) >2. Local changes can require non-local code restructuring. Not if you format well :-) . >Adding an else-clause can require you to restructure the {}'s. Nope: if (a) foo; /* add else here */ if (b) ... if (a) foo; else { stuff; more stuff; } if (b) ... >Often, for(;;) loops are written with *no body*, and adding additional >commands can require major restructuring of the loop. I do not understand this either. >3. Include files are hard to keep track of, nest, and structure in a >big project. Modules & Interfaces seem to work much better. Any automated scheme (Mesa's is semi-automated) tends to work better than any manual scheme. Some people have developed automated schemes for C, although since it is not handed down from on high, it can be hard to get people to use your favourite. >4. I really miss the way MESA treats records as first class types. This really is a serious problem in C. It is, however, partly fixed in the dpANS, where automatic aggregates may be initialised. There are still no generic aggregate constructors, nor is there any really decent way to deal with unions (the first-member rule is not useless but does not do anywhere near enough). >5. I miss MESA INLINE procedures. They are simpler and (often) more >efficient than C's macro expansions. Actually, they are not always simpler. What are the semantics of the static variable below?: inline int unique_integer() { static int next; return (next++); } The intended semantics are obvious, but providing exactly those semantics is not trivial, since all users of the inline function, regardless of source file, must use the same `next', which must be different from the `next' of any other inline function. (GCC, incidentally, does this right, when it does it at all. The gcc we compiled a month or two ago on a Sun broke when asked to deal with inline functions, although the code it tried to generate would have been right---if that makes any sense to you.) >6. C has no provision for callback procedures (you need nested >procedures and the right semantics for the 'display'). So data >abstraction is hindered in C. You need neither, and I personally believe that those sorts of nested variables (if not the procedures themselves) are a misfeature---they add a fair bit of expense to the calling sequence (gotta maintain them displays or static links) and account for very few actual variable accesses. Better to use (semi-)hidden pointer parameters a la C++, which also effectively gives you closures. Someone else complained about a lack of `var' parameters. Again, I think this is a very *good* thing. One of the worst problems with reading Pascal code is that every procedure and function call might potentially alter every variable argument. If I see a section of C code that reads munch(a, b, &c, d, e + 7); I can assume that it cannot change a, b, and d (unless they are arrays, arrh), while the same statement in Pascal, stripped of its warning `&', tells me no such thing. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
gillies@p.cs.uiuc.edu (08/15/88)
I will elaborate, since I think parts of my posting were misunderstood: >Adding an else-clause can require you to restructure the {}'s. Here is an extreme example, and many others exist: if (a) if (b) if (c) do_something_useful(); /* Add an else-clause for the second -if- produces */ if (a) { if (b) { if (c) do_something_useful(); } else c_sucks_the_big_wazoo_sometimes(); } Look at all the braces which had to be added to make the else clause bind correctly! >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++); /* <theoretical example only> */ } Notice how I had to restructure the looping condition to make this change. Things can be much worse with a more complicated loop. >6. C has no provision for callback procedures (you need nested >procedures and the right semantics for the 'display'). So data >abstraction is hindered in C. Let us say I have a data-abstract code package that exports a procedure that traverses a dictionary data structure. The format of the data structure is proprietary. Callback requires ONE level of procedural nesting for each nested loop. 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..... Callback procedures are a nice way of supporting abstract enumeration when the language lacks something like CLU's explicit enumerators. Don Gillies, Dept. of Computer Science, University of Illinois 1304 W. Springfield, Urbana, Ill 61801 ARPA: gillies@cs.uiuc.edu UUCP: {uunet,ihnp4,harvard}!uiucdcs!gillies
nagler%olsen@unizh.UUCP (Robert Nagler) (08/16/88)
Question: Do people like these conversations? I seems to me that C vs. M2 on an M2 mailing list is a bit silly. I realize the need for open debate, but anyone who has done serious work in M2 (Son of Mesa) would undoubtably agree that M2 is superior to C. Please respond to me directly to keep down the temperature on the net. Sorry for the length, but it seems there is a lot of misinformation going around. Chris Torek writes: > Mesa is considerably more useful for writing Real Programs than is > Modula-II, if only because its I/O really is standard (or at least > only has to deal with one O/S!). So this is more of a Mesa-vs-C: There is more than enough supporting evidence to the contrary since DEC-SRC (loaded with PARC people) have been using M2+ which merely has built-in monitors, exceptions, and generic pointers. They also did an AGC, but I don't see as a must (as I've mentioned a while ago in another note). We do "Real Programs" here which deal with Real Customers on a 24 hour a day basis all year long (except when the A/C goes out). Everyone who sees our work says: "You did this ALL in M2?" Funny, how empirical evidence kills even the most die-hard blow-hards. >>2. Local changes can require non-local code restructuring. >Not if you format well :-) . What if you change a parameter from by-value to by-reference (for performance reasons). In M2, this would simply be a change to the def mod. In C, you would have to change *every* single reference to that procedure. Ah yes, we always pass our parameters by reference in C, therefore this should never occur? [See below] >>Adding an else-clause can require you to restructure the {}'s. >Nope: Yup: if (a) if (b) do_this; goes to: if (a) if (b) do_this(); else do_that(); In M2, this couldn't happen (in Pascal, it could). >>3. Include files are hard to keep track of, nest, and structure in a... >Any automated scheme (Mesa's is semi-automated) tends to work better... Wrongo! The advantage of Mesa is *compiled* interfaces. This is the most significant advance in programming since type checking (in my humble opininion). If someone changes a structure in C, you may never know about it (I've seen "make -t" abused too often). Compiled interfaces eliminate this problem. >>5. I miss MESA INLINE procedures. They are simpler and (often) more >>efficient than C's macro expansions. >Actually, they are not always simpler. What are the semantics of the >static variable below?: > inline int unique_integer() { static int next; return (next++); } In M2 (Mesa), you would never write a procedure this way. Module variables are used instead of static variables. In fact, you wouldn't write a macro this way in C? What language is that? >Someone else complained about a lack of `var' parameters.... > munch(a, b, &c, d, e + 7); >I can assume that it cannot change a, b, and d.... Almost every parameter (>int) in C is passed by-reference, so you never know what's being modified. [This convention seems to have been adopted by an earlier inadequacy in C compilers. Kind of like "stay away from enumerated types."] My biggest problem with Unix man pages is that you never know what the function is expecting unless you read the detailed descriptions of all the parameters). One of my favorite features of C is: scanf( "%d", i ); I can't tell you how many times I have had to help novice C programs with this. If "i" is on the stack, you get pretty random results. scanf( "%s\n", "Some string to print" ); Is also interesting. Sometimes people make mistakes, but far be it for the compiler to tell them that they are stupid. -- END Reply -- BEGIN A Different Perspective The following is *my* comparison between C and M2. I have taken a typical procedure in Unix (which I was so fortunate enough to use the other day) and declared it in M2 and C. The C declaration is first. The function "setitimer" was taken from the "man page" and the supporting declarations were taken from an include file "sys/time.h" comments were included (were appropriate). struct timeval { long tv_sec; /* seconds */ long tv_usec; /* and microseconds */ }; /* * Names of the interval timers, and structure * defining a timer setting. */ #define ITIMER_REAL 0 #define ITIMER_VIRTUAL 1 #define ITIMER_PROF 2 struct itimerval { struct timeval it_interval; /* timer interval */ struct timeval it_value; /* current value */ }; setitimer(which, value, ovalue) int which; struct itimerval *value, *ovalue; Is "value" modified? What is the return value of the function? Ah yes, -1 means false and 0 means true. WAIT, 0 means false in C and non-zero means true. However, I needless to say had to read the man page several times to figure out that "it_value" was relative to now and that "it_interval" would be reloaded *after* "it_value" expired. Believe it or not, I have looked at this man page on several different occasions and still have trouble remembering which way is up. In M2, I would define the procedure as follows: TYPE Time = RECORD seconds : CARDINAL; microseconds : CARDINAL; END; TimerSetting = RECORD interval : Time; (* Value to reload after "first" expires *) first : Time; (* Relative amount to "now" for expiration *) END; TimerTypes = ( (* Three distinct timers per process which measure: *) real, (* real time *) virtual, (* time when process is in user code *) profile (* virtual and time when process runs in kernel. Warning: SIGPROF interrupts system calls! *) ); PROCEDURE setitimer( which : TimerTypes; value : TimerSetting; (* If value.first = 0, turn off timer *) VAR oldValue : TimerSetting ) : BOOLEAN; (* FALSE ==> failure; see errno (ugh!)*) The first thing to note is that there are no descriptions of the names! I don't have comments like "/* and microseconds */" to clutter up the brain. Comments contain *new* information which generally contains some semantic content that could not otherwise be provided by the M2 part of the declaration. With this (C-like) M2 declaration, I believe I could have guessed how setitimer works. We use a lot of C-Unix interfaces in our Modula-2 via special def mods supported by the Sun Compiler (thanx Sun). In all cases, we immitate the C declarations as they are defined in C. In no cases, have I been unable to translate the C declarations. However, if I were given the reverse to do in C, you would be hard-pressed to do some of the following: PROCEDURE Insert( key : ARRAY OF SYSTEM.BYTE; (* BTW, can I do HIGH( key ) in C? *) ); TYPE IntPtr = POINTER TO INTEGER; SomeProc = PROCEDURE ( Object, ) : IntPtr; PROCEDURE Foo( bar : Object; val : CARDINAL ) : SomeProc; TYPE Values = [ 0 .. 65535 ]; (* int? short? char? Your guess... *) Bits = SET OF [ 0 .. 15 ]; (* ditto *) I have spoken before about these trivialities as above as well as the real features of M2. I am interested in honest answers. How many of the "C" people who read this list have programmed in Modula-2 (or Mesa for that matter)? Do you want to know what real problems are? Do you care? I've programmed in C for five years and in Modula-2 for four years. I liked C, because it freed me from Pascal. I like M2, because it allows me to design and build systems quickly and efficiently. With C I am still trying to figure out which makefiles I *must* include before I include the one I want (among other things). What's bad about M2? There are two flavors of implementations: one-pass and two-pass. The two-pass systems are more mature. The one-pass systems are probably better, but it is hard to use them because we wrote 500 modules using the two-pass style (anyone written an automatic converter yet?). Most of the M2 implementations don't do: automatic inlining (not so hard), register allocation (local would be fine), any amount of code optimization, and/or code elimination. The problem is that no one takes a half a minute to think what it would take to make a good M2 compiler. (I'll supply a empirically proven portable library free of charge.) Forget the past, one company says, and implements a great one-pass compiler. Another company rewrites their compiler/linker (from scratch) and now it doesn't link our system so we have to stick with the old version. However, almost every PC M2 compiler comes with its own handy-dandy text editor so that it will sell to the people who use it once and then shelve it. Short-sightedness (both forwards and backwards) is killing M2. The language itself isn't nearly as bad as the implementation madness. Even with all of these problems I can still write a program that uses >100 light-weight processes, screen graphics, and user interrupts and port it from the PC to the Sun in under an hour. The LWP model was ported in a couple of days from the PC to the Sun. You just don't know until you've tried it. M2 ain't so bad. Once again, sorry for the length. I hope this helps someone. Rob Nagler mcvax!olsen!nagler@uunet.uu.net Olsen & Associates/Research Institute for Applied Economics/Zuerich
chris@mimsy.UUCP (Chris Torek) (08/16/88)
>Chris Torek writes: >>Mesa is considerably more useful for writing Real Programs than is >>Modula-II, if only because its I/O really is standard (or at least >>only has to deal with one O/S!). So this is more of a Mesa-vs-C: In article <8808151857.AA05996@klaus.olsen.uucp> nagler%olsen@unizh.UUCP (Robert Nagler) writes: >There is more than enough supporting evidence to the contrary since >DEC-SRC (loaded with PARC people) have been using M2+ which merely >has built-in monitors, exceptions, and generic pointers. `Merely'?! >>>2. Local changes can require non-local code restructuring. >>Not if you format well :-) . >What if you change a parameter from by-value to by-reference (for >performance reasons). You have me there (but then that was why I put in the `:-)' ). But this is irrevocably tied in with the `var' parameter declaration format, which I personally hate. >>>Adding an else-clause can require you to restructure the {}'s. >>Nope: >Yup: > if (a) > if (b) > do_this; >goes to: > if (a) > if (b) > do_this(); > else > do_that(); But (says he) the original code is not `well-formatted': if (a) { if (b) { do_this; } } Adding the else is now trivial. Basically, this just means using full bracketing even though the language does not provide fully bracketed constructs (I happen to like fully bracketed syntax a la Algol, even to the silly sdrawkcab spellings). >>>3. Include files are hard to keep track of, nest, and structure in a... >>Any automated scheme (Mesa's is semi-automated) tends to work better... >Wrongo! The advantage of Mesa is *compiled* interfaces. Funny, our Mesa people seem to spend about half their time working around this `advantage', since Xerox keeps sending incompatible revisions of things. (Not a problem with the language!) >This is the most significant advance in programming since type checking >(in my humble opininion). Hm? It *is* type checking. Mesa embeds it in the language by providing syntax and semantics for it. You still have to use it by hand (hence `semi-automated'). >>>5. I miss MESA INLINE procedures. They are simpler and (often) more >>>efficient than C's macro expansions. >> inline int unique_integer() { static int next; return (next++); } >In M2 (Mesa), you would never write a procedure this way. >Module variables are used instead of static variables. In fact, >you wouldn't write a macro this way in C? What language is that? It is one of the GNU C compiler's (numerous) extensions to C. A Mesa module variable is simply a static variable that is local to a module, just like C file-static (or if exported, just like C file-global). As such, it cannot be part of an inline function that is also accessible-but-still-in-line outside that module. GCC gets this right, by allowing inline functions outside modules (and playing naming games to get it to work at all). >>Someone else complained about a lack of `var' parameters.... >> munch(a, b, &c, d, e + 7); >>I can assume that it cannot change a, b, and d.... >Almost every parameter (>int) in C is passed by-reference, so you >never know what's being modified. I disagree with this claim, but even granting it (temporarily! :-) ), while I do not know what *is* being modified, I *do* know what *cannot* *be* modified, which to me is at least as important. >... My biggest problem with Unix man pages is >that you never know what the function is expecting unless you read >the detailed descriptions of all the parameters). Funny, the same would be true if it were M2 and the parameters were declared `var', without any other changes. What is needed is access rights (e.g., Ada---much as I think that language is overdone). >One of my favorite features of C is: > scanf( "%d", i ); # this is one reason why `lint' should be run every time you run the # compiler (not that there are no reasons not to run lint every time): % cat t.c main() { int i; scanf("%d", i); exit(0); } % lint t.c t.c: t.c(1): warning: scanf argument is type (int) rather than pointer (arg 2) t.c(1): warning: i may be used before set scanf returns value which is always ignored % A deficient implementation is not a reason to complain about the language! And, similarly, a good but extended implementation, such as your M2+, or any other M2 with the (remarkably few, but sometimes remarkably large, as in I/O) holes filled in is not a reason to praise the language. [comparison of 4.2BSD setitimer declarations vs. newly created equivalent M2 declarations] >... I needless to say had to read the man page several times to figure >out that "it_value" was relative to now and that "it_interval" would be >reloaded *after* "it_value" expired. In other words, the names and comments in the first part of the manual entry---the part where the type of the function (including arguments) is described---are insufficient. You then provide a different version written in M2 syntax, with *different names* and *different comments*: >The first thing to note is that there are no descriptions of the names! >I don't have comments like "/* and microseconds */" to clutter up the >brain. Comments contain *new* information which generally contains >some semantic content that could not otherwise be provided by the >M2 part of the declaration. With this (C-like) M2 declaration, >I believe I could have guessed how setitimer works. And from changing the names and the comments, you conclude that the M2 declaration (which you wrote, so naturally you will understand it) is inherently superiour to the C declaration? C declarations do have their problems. The major complaint seems to be that they are overly terse; I also believe that the various pre- and post-fix styles are part of the problem. If `pointer' always came after the name, or if `function returning' always came before, most of the mysterious parentheses in C declarations like void (*signal(int sig, void (*sigfn)(int sig)))(int sig); /* to use the dpANS declaration syntax */ would go away. But you seem to imply that M2 would somehow automatically improve naming conventions and commenting, which I find unlikely. >PROCEDURE Insert( > key : ARRAY OF SYSTEM.BYTE; (* BTW, can I do HIGH( key ) in C? *) > ); I have forgotten what HIGH(key) is supposed to do, but assuming that it does the obvious---returns the size of the array, in whatever units--- the answer is `yes and no': you can get the size, but the caller must provide the size; the called procedure can no longer obtain it. This is done so that the procedure call conventions need not provide array descriptors, and because arrays are second-class data objects in C (which, incidentally, *is* one of C's serious shortcomings). void Insert(void *key, size_t keylength); /* again, dpANS syntax. also legal is: void Insert(void *, size_t) but I think not using parameter names is dumb */ >TYPE > IntPtr = POINTER TO INTEGER; > SomeProc = PROCEDURE ( > Object, > ) : IntPtr; >PROCEDURE Foo( > bar : Object; > val : CARDINAL > ) : SomeProc; (I do not understand the syntax `PROCEDURE(Object,)'. Since M2 does not have first class functions, I assume that SomeProc is actually a pointer to some sort of procedure. Anyway:) typedef int *IntPtr; typedef IntPtr (*SomeProc)(XXX);/* where XXX depends on what the above means */ SomeProc foo(Object bar, unsigned val); >TYPE > Values = [ 0 .. 65535 ]; (* int? short? char? Your guess... *) > Bits = SET OF [ 0 .. 15 ]; (* ditto *) Ranges are another real shortcoming in C. But I do wonder what you get if you declare, e.g., TYPE small = [0..12]; VAR a, b : small; ... a := 9; b := 11; a := a + b; How is this defined? I hope it must be a (presumably run-time) error, with a compile-time warning allowed if the compiler is good. (If the compiler is good enough and can prove that the code above must execute, a compile-time error would be even better.) For minimum ranges, the dpANS says that an `unsigned short' must be able to hold the values in 0..65535, hence it would suffice for both types above. Speaking of sets, has M2 fixed the Pascal hole about sets? That is, must the compiler do the right thing for TYPE bigset = SET OF [0..41]; ? (Pascal allows compilers to abort if the set contains more bits than fit in a `machine word'---whatever that may be :-) . While some implementations allow larger sets, it is NOT part of the language, and this must be considered in any language discussion. More below:) >What's bad about M2? There are two flavors of implementations: >one-pass and two-pass. The two-pass systems are more mature. >The one-pass systems are probably better, but it is hard to use them >because we wrote 500 modules using the two-pass style (anyone written >an automatic converter yet?). Why should one need a converter? Either forward declarations are unnecessary and the compiler had better do the right thing no matter how many passes it uses internally, or they are required and a two (or N) pass compiler had better complain if they are not there. Anything else is not M2 (unless the language definition really says that compilers can complain depending on how they are implemented!). [more woes about existing implementations deleted] The implementation is not the language. No doubt there are some terrible implementations of M2, as well as some good ones. The same is true of C. The biggest hole in the language is that it does not provide any I/O. Neither does C-the-language-syntax-and-semantics, but stdio is considered a part of `the language'. If there were a standard I/O package for M2, the hole would vanish. As a `for instance', consider VAX/VMS FORTRAN. By all accounts this is a wonderful implementation, and is a language in which it is easy to write reasonable code that runs quickly and does whatever task is at hand. On the other hand, the 4.2BSD f77 FORTRAN compiler is slow, buggy, produces lousy code, and has a weak library. Neither one says anything about the language *per se*. Basically, my point can be stated thus: If you have to extend the language---either in internal syntax or in support libraries---to accomplish what you must do, the language is insufficient for your needs. Since literally everyone has to extend Modula II for anything that reads input or prints a result, that language is insufficient for those needs. Clearly, if you finish your task, the (new) language *is* sufficient for those needs. Anyway, while it is possible to compare specific features of one language with those of another, I think blanket comparisons will always fail in some way. I will not claim that C is `better' than M2, nor vice versa. I *will* say that I like this feature, or dislike that one. I will also go so far as to say that discarding some features in favour of `better' ones from another language might not improve the first language after all. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
chris@mimsy.UUCP (Chris Torek) (08/16/88)
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++); /* <theoretical example only> */ >} 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
rion@wdl1.UUCP (Rion Cassidy) (08/16/88)
gillies@p.cs.uiuc.edu writes: >When I first came to grad school, and began writing C code again, I >was unexpectedly disappointed. I had grown used to a Modula-II like >language (MESA). Some of the things that really bothered me about C >were: [complaints deleted] You didn't say where you learned Mesa, but since Xerox is the only place that uses it, I'd assume you came from there. Your comments are interesting because when I interveiwed with Xerox they said that Mesa was more like C than Pascal or M-2. Rion Cassidy rion@wdl1.arpa
marti@ethz.UUCP (Robert Marti) (08/18/88)
In article <8808151857.AA05996@klaus.olsen.uucp>, nagler%olsen@unizh.UUCP (Robert Nagler) writes: > We use a lot of C-Unix interfaces in our Modula-2 via special definition > modules supported by the Sun Compiler (thanx Sun). ^^^^^^^^^ The thanks go to Oce Wissenschaftlisches Forschungsinstitut, Zurich, and in particular to Leo Geissmann. > What's bad about M2? There are two flavors of implementations: > one-pass and two-pass. Make that one-pass and multi-pass. > Rob Nagler mcvax!olsen!nagler@uunet.uu.net > Olsen & Associates/Research Institute for Applied Economics/Zuerich -- Robert Marti Phone: +41 1 256 52 36 Institut fur Informatik ETH Zentrum CSNET/ARPA: marti%ifi.ethz.ch@relay.cs.net CH-8092 Zurich, Switzerland UUCP: ...uunet!mcvax!ethz!marti
gillies@p.cs.uiuc.edu (08/18/88)
In article <79500010@p.cs.uiuc.edu> gillies@p.cs.uiuc.edu writes: >>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..... Written 2:32 am Aug 16, 1988 by chris@mimsy.UUCP in comp.lang.modula2 >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 :-) .) (C programming example omitted) Your example isn't type-safe, although it's same solution I use. I only asked the question because I knew it couldn't be answered in C. Your coding trick starts crumbling with nested callback loops (something you've probably never considered because it's so tedious in C!) Maybe it can be done type-safely in C++. I hope it doesn't take one class per callback proc or one per nest! I am leery of C++, since it resembles MESA, in that it's a cooking pot with everything but the kitchen sink thrown in! The result is an even bigger language that's hard(er) to master (than C). I think MESA descended from Algol and BCPL. It happens to be enough of a superset of PASCAL that a PASCAL->MESA translator exists. If a Xerox interviewer said that MESA was closer to 'C', he was massaging your fears and/or stretching the truth. The joke at Xerox is that Modula-2 "is all the MESA Nicklaus Wirth could remember when he returned to Switzerland" (he was a visiting scientist at Xerox). Mesa lacks the pointer arithmetic of C, but has all the arithmetic operators like :?, and also & ! >> << through the InLine procedure library, with no loss in performance. Don Gillies, Dept. of Computer Science, University of Illinois 1304 W. Springfield, Urbana, Ill 61801 ARPA: gillies@cs.uiuc.edu UUCP: {uunet,ihnp4,harvard}!uiucdcs!gillies
wyle@solaris.UUCP (Mitchell Wyle) (08/18/88)
>because we wrote 500 modules using the two-pass style (anyone written >an automatic converter yet?). Most of the M2 implementations >don't do: automatic inlining (not so hard), register allocation (local >would be fine), any amount of code optimization, and/or code elimination. The Sun M2 compiler version 2.1 uses the Sun global optimizer for peep-hole and shared library optimization. I admit it's only in the code generation, and not earlier, but it's better than nothing. >The problem is that no one takes a half a minute to think what it >would take to make a good M2 compiler. (I'll supply a empirically >proven portable library free of charge.) Forget the past, one Which library are you talking about? >Rob Nagler mcvax!olsen!nagler@uunet.uu.net >Olsen & Associates/Research Institute for Applied Economics/Zuerich -- -Mitchell F. Wyle wyle@ethz.uucp Institut fuer Informatik wyle%ifi.ethz.ch@relay.cs.net ETH Zentrum 8092 Zuerich, Switzerland +41 1 256-5237