Path: news.daimi.aau.dk!news.daimi.aau.dk!eernst From: eernst@quercus.daimi.aau.dk (Erik Ernst) Newsgroups: comp.lang.beta Subject: Re: Overriding procedural patterns Date: 10 May 1995 13:49:59 GMT Organization: DAIMI, Computer Science Dept. of Aarhus Univ. Lines: 264 Message-ID: References: <3o315c$9he@pulm1.accessone.com> <3odefd$hbp@belfort.daimi.aau.dk> NNTP-Posting-Host: quercus.daimi.aau.dk In-reply-to: olevi@daimi.aau.dk's message of 5 May 1995 14:58:53 GMT In article <3odefd$hbp@belfort.daimi.aau.dk> olevi@daimi.aau.dk (Ole Villumsen) writes: Baiss E. Magnusson writes: >> I am troubled by the Beta definition that a virtual pattern can only be >> extended and cannot be completely redefined. The complete over-riding of >> the enclosing procedure pattern seems to me to be a fundamental need. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ I guess you mean "the superpattern" here? > The usual hack to achieve this goes like: > > super: (# virtual:< (# doneInInner: @Boolean; > do inner; > (if doneInInner//false then (* do it my way *) > (* else do nothing *) > if); > #); > #); > > sub: super (# virtual::< (# do (* do it my way *) > true -> doneInInner; (* so super won't do it *) > #); > #); > > It only requires a little foresight when writing the super-pattern, > of course. In many cases where overriding appears to be called for, I think changing the inheritance hierarchy may work just as well: ---------- (* What we think we want *) shape: (# draw:< object; (* Ah, we can't say it's "abstract" .. *) ... #); polygon: shape (# draw::< (# do <> #); numberOfVertices:< integerValue(# do ... #); ... #); square: polygon (# draw:!< (# do <> #); (* __^__ new and wonderful syntax for override!! *) numberOfVertices::< integerValue (# (* Well, this is just another kind of override, isn't it? *) do (if value<>4 then (failure,'A square must have 4 vertices')->stop; if); #); ... #); ---------- (* Another possibility *) shape: (# draw:< object; ... #); polygon: shape (# (* draw not furtherbound here *) ... #); variablePolygon: polygon (# draw::< (# do <> #); ... numberOfVertices:< integerValue(# do .. #); #); square: polygon (# draw::< (# do <> #); ... #); ---------- (* EOCE, i.e. end-of-contrived-example *) Of course, this "gut feeling" of mine doesn't get acceptable backing from any number of examples .. but perhaps I'm not the only one to think like this? When we *really do need* override semantics, it gets clumsy. For instance, I don't like the run-time overhead imposed by executing the equivalent of (if not doneInInner then (* doneInInner _is_ false *) <>; if); respectively <>; true->doneInInner; (if not doneInInner then <>; (* unreachable code *) if); where we could have had <>; respectively <>; especially when it is executed very often and the "implementation"s are very simple. It's not that self-evident, either, that the run-time test of 'doneInInner' actually gives a pattern-wise statically known result; one might think that 'doneInInner' could vary between two objects of the same type, or from time to time for the same object. I once wrote a proposal for an alternative inheritance mechanism ("weak inheritance") which would allow override semantics whereever wanted, and which would introduce more flexible method-combination schemes. Weak inheritance works exactly like normal inheritance wrt scope rules, enter-lists, and exit-lists. Only the combination of the do-parts up through the chain of super-patterns is affected. Nobody (else :-) liked it. Anyway, here is a sketch of it: top: (# a:< (# do '(1'->puttext; INNER; '1)'->puttext; #); #); middle1: top (# a::< (# do '(2'->puttext; INNER; '2)'->puttext; #); #); leaf1: middle1 (# a::< (# do '3'->puttext #); #); middle2: top (# a:!< (# do '(4'->puttext; INNER; '4)'->puttext; #); #); leaf2: middle2 (# a::< (# do '5'->puttext #); #); middle3: top (# a:!< (# do '(6'->puttext; OUTER; '6)'->puttext; #); #); leaf3: middle3 (# a::< (# do '7'->puttext #); #); do (&top[]).a; (* "(11)" as usual *) (&middle1[]).a; (* "(1(22)1)" as usual *) (&middle2[]).a; (* "(44)" aha? It's ":!<" .. *) (&middle3[]).a; (* "(6(11)6)" ah, yes, I see *) (&leaf1[]).a; (* "(1(232)1)" as usual *) (&leaf2[]).a; (* "(454)" yes, OK *) (&leaf3[]).a; (* "(6(171)6)" what?! 7 in there!? *) So what does this mean? A couple of applications are ---------- (* overriding *) a: (# p:< (# do <> #)#); b: a(# p:!< (# do <> #)#); (* "OUTER" not used *) ---------- (* non-standard method combination .. think of CLOS, e.g. *) top: (# p:< (# do <> #)#); (* not concurrency-safe *) middle: top (# p:!< (# (* add a 'before' and an 'after' method to 'p' *) before:< (# do semaphore.p; INNER #); after:< (# do INNER; semaphore.v #); do before; OUTER; after; #); #); leaf: middle (# p::< (* enclosed within critical region *) (# before::< (# do <> #); after::< (# do <> #); do <> #); #); ---------- (* A couple of special cases *) a: (# do OUTER #); (* Static semantic ERROR: "OUTER" is only * allowed in connection with weak inheritance *) b: (# do INNER #); c: b(# do OUTER #); (* same problem *) d:! (# ... #) (* OK, but hardly interesting; weak inheritance * from 'object' is indistinguishable from * standard inheritance from 'object' *) e: (# do 'e'->put; INNER #); f:! e(# do 'The hen or the '->puttext; OUTER; INNER #); g: f(# do 'g'->put #); (* Yes, it prints "The hen or the egg"! *) 15 -> !myfunction(# do abc; OUTER; xyz #) -> x; (* OK *) ---------- (* More than one level of weak inheritance *) a: (# do '1'->puttext; INNER; '1'->puttext #); b:! a(# do '2'->puttext; OUTER; '2'->puttext #); c:! b(# do '3'->puttext; OUTER; '3'->puttext #); d: c(# do '4'->puttext #); do a; (* "11" *) b; (* "2112" *) c; (* "321123" *) d; (* "3214123" *) Think of it this way: Normal inheritance: Executing the dopart of a pattern means executing the dopart of its super-pattern with its own dopart as a _callback_ to execute for each occurrence of "INNER". The most specialized pattern binds its "INNER" to DO-NOTHING. Weak inheritance: Executing the dopart of a pattern means executing this dopart with "OUTER" bound to the dopart of its super-pattern which in turn binds its "INNER" to the same as the "INNER" of this pattern. The most specialized pattern binds its "INNER" to DO-NOTHING. So, intuitively, the meaning of "INNER" is propagated up through all levels of weak inheritance. The basic idea is to provide a general mechanism which is orthogonal to the other aspects of BETA, reasonably simple to understand and use, and that makes it possible to do more flexible kinds of method combination than what is (directly) supported today. I also think it should allow for an efficient implementation, even though it demands some changes compared to the methods used today in Mjølner BETA. Then again it might help in implementing virtual prefix and other as-yet-unsupported kinds of inheritance. .. comments are looked forward to! -- Erik Ernst eernst@daimi.aau.dk Computer Science Department of Aarhus University, Denmark