home *** CD-ROM | disk | FTP | other *** search
- From: sfk@otter.hpl.hp.com (Steve Knight)
- Date: Sun, 20 Dec 1992 15:00:33 GMT
- Subject: Re: POP syntax
- Message-ID: <116670036@otter.hpl.hp.com>
- Organization: Hewlett-Packard Laboratories, Bristol, UK.
- Path: sparky!uunet!zaphod.mps.ohio-state.edu!sdd.hp.com!hpscit.sc.hp.com!hplextra!otter.hpl.hp.com!otter!sfk
- Newsgroups: comp.lang.pop
- References: <57553@dime.cs.umass.edu>
- Lines: 285
-
- Here's a lengthy response to Robin's provocative and interesting
- message. Being a response, it moves around several topics which
- should probably be in distinct messages. However, there is a common
- thread of possible revisions to POP and revisiting old design
- decisions that makes them hang together is quite an intriguing way,
- I believe .....
-
- Robin Popplestone writes about POP's list syntax:
- > Probably if we had
- > had a supply of curlies in the early days we would have used {this is a
- > list} as being somehow suitable for lists as set-like things (and perhaps
- > we would have been wrong - certainly it is no help to think of an
- > application as a set!, [text deleted]
-
- It is interesting to see that GLOW took the route of using braces
- and brackets to distinguish quoted and unquoted lists. It seems that
- POP language designers are keen to be able to conveniently denote
- nested list constants. My own view is similar to Robin's -- it is waste
- of precious bracketing symbols and not mnemonic to use braces in this
- way.
-
- I was fascinated to watch Chris Dollin, the designer of Pepper, slowly and
- reluctantly accepting the need for list quasi-quoting. His solution was
- to integrate the list and vector syntax into the single quote context.
- So in Pepper, you can write "[ ... ]" to achieve the same effect as POP-11's
- [ ... ] and, naturally enough, "{ ... }" to do the same as POP-11's { ... }.
- (There are minor semantic differences discussed below.)
-
- The result in Pepper is that there is only one quoting system which is
- introduced by the quote symbol '"'. The disadvantage is that the quote
- is really a quasi-quote and more complex to learn -- although not as hard
- as POP-11's list syntax since it has a more regular semantics and a single
- escape mechanism.
-
- One of the most powerful arguments for the kind of solution Pepper has
- pioneered is the rationalisation of quoting semantics. Here's a question
- for all you budding POP-11 experts out there (yes, I'm talking to you
- James) that illustrates the needless complexity of its quoting
- semantics. Try to answer without referring to any manuals and without
- typing the answer to a POP-11 interpreter ...
-
- What are A, B, C, and D bound to after the following statements are executed?
- vars A, B, C, D;
- vars L = [a b c];
- vars V = {a b c};
- {1 ^^V 2} -> A;
- [1 ^^L 2] -> B;
- {1 ^^L 2} -> C;
- [1 ^^V 2} -> D;
-
- And if you got that right, try this one:
- vars L = conspair( "a", "b" );
- [1 ^^L] -> A;
- [1 ^^L 2] -> B;
-
- As I alluded to above, the separation of quoting from list and vector
- construction, as in Pepper, makes the above irregularities vanish. Since
- the semantics of the anti-quotation operator '^^' are defined with
- respect to quotation rather than list or vector construction.
-
-
- Robin then moves onto a few of his least favourite things:
- > My pet HATE is having to declare my lexicals as lvars. I have from time to
- > time tried to do little hacks to avoid this, but have not got a stable one.
- > Also, while we are at it, the control variable of a for loop should be
- > automatically local to the loop. All these things are being incorporated in
- > Pepper.
-
- I completely agree. I think that Scheme shows the correct way to do
- loop variables -- it makes them local to the loop and the loop-start
- is outside of that local context. As a consequence, each time around the
- loop, the control variable is declared. This means that simple loops
- such as
- for i in [a b c] do
- procedure(); i endprocedure
- endfor
- would deliver 3 separate procedures instead of just one.
-
-
- But then Robin starts to edge off the rails by complimenting ML:
- > Another thing I would like is a neat, concise syntax for anonymous
- > functions. The old LAMBDA .......END construct was painful enough, without
- > being replaced by procedure.....endprocedure. ML's fn <patt> => <body> is
- > neat.
-
- It might well be nice to have a concise pair of procedure brackets (and I
- tend to use fun ... endfun) but let's not have the ML model. ML makes
- several dire misdesign moves that makes the kind of mistakes I have been
- discussing in POP-11 look like models of excellent design. The "fn"
- syntax is a prime example. Suffice it to say that there are two serious
- blunders in the design of the anonymous function syntax in ML which
- are (1) the lack of a closing keyword (2) using the same pattern continuation
- symbol (the vertical bar '|') as the "case" and "fun" constructs where
- anonymous functions frequently appear. The result of this is that it
- is usual to write
- (fn <patt> => <expr>)
- in order to prevent unwanted ambiguity from creeping in. Come back LISP,
- all is forgiven!
-
- And I would also like to see the "anonymous" procedure syntax extended to
- allow the addition of a name for the procedure. e.g.
- procedure fact( n );
- if n <= 1 then 1 else n * fact( n - 1 ) endif
- endprocedure
- And this wouldn't merely be describing the -pdprops- of the procedure. The
- intention would be that the procedure would be able to refer to itself by
- that label.
-
- What is the point? Well, it means that the definition of the factorial
- function given above is immune from the assignment to "fact".
- Normally writing directly recursive functions means that they are
- vulnerable to manipulation of the top-level identifer. This device
- ensures that they keep their meaning. And this
- property means that directly recursive functions can often be significantly
- optimised. This is one of those lovely additions that is completely in
- harmony with the language and benefits all users, direct and indirect.
-
- [For compiler writers, note that this means that tail-call optimisation
- is safe.]
-
-
- Finally, Robin moves onto a subject which he clearly worries about
- a great deal -- the disparity between POP-11 and functional languages
- in the way they effect partial application:
- > An interesting point is whether there is any way of getting rid of the
- > distinction between application and partial application, which is not a
- > fundamental distinction of the lambda calculus, but is only (there) an
- > implementation issue.
-
- I don't think this is the right way of looking at POP-11 at all. The
- proper comparison is that a multi-argument POP procedure is like a
- function with a tupled argument. POP's partial application freezes
- in the first element of the tuple yielding a function whose domain
- is one type shorter.
-
- To illustrate this, let's write a trinary function in both POP-11 and
- an imaginary version of ML that dynamically typechecks.
-
- define foo( z, y, x ); <EXPR> enddefine;
-
- fun foo( x, y, z ) = <EXPR>;
-
- Now we specialise on the argument x, with x = X.
-
- vars fooX = foo(% X %);
-
- val fooX =
- let fun freeze( frozval, func ) tuple =
- let val L = tuple_to_list tuple;
- val T = list_to_tuple( frozval :: L )
- in
- func T
- end;
- in
- freeze( X, foo )
- end;
-
- Now we can see the real advantage of partial application! There's no
- equivalent in strongly typed functional languages because tuples are
- not abstracted over in the type system. But in a dynamically typed
- system, such as POP, the advantages are clear.
-
- In a language like ML, there is always a tension between defining a
- function in curried form or tupled form. This is because the ML
- syntax is geared up to deal more conveniently with tupled functions
- but curried functions work with partial application more elegantly.
-
- But in a dynamically typed language such as POP-11, this tension
- disappears because we can write our functions in tupled form *and*
- partially apply them. So it is very uncommon to see code such as
- this
- define foo( z );
- procedure( y );
- procedure( x );
- <EXPR>
- endprocedure
- endprocedure
- enddefine;
-
- The closest one gets to writing this is when you wish to partially
- evaluate on the basis of the frozen argument. An example of this
- might be the form of a regular expression matcher. One would typically
- write
- define re_match( regexp );
- lvars fast_matcher = re_compile( regexp );
- procedure( target );
- .... fast_matcher( target ) ...
- endprocedure
- enddefine;
-
-
-
- > A related problem is that partial application
- > in POP-11 "works backwards", as compared with the lambda calculus (and
- > therefore ML and Haskell). This is a real problem in mixed language
- > working, because there are actually some arguments that are more usefully
- > partially applied than others. E.g. we (Burstall and I) chose the order of
- > arguments in maplist so that the function argument could be bound by
- > partial application. The order is OPPOSITE in ML.
-
- If one accepts my comparison between partial application and tuple
- instantiation, then this is plainly the wrong comparison. The strongest
- statement that you can make is that people (such as Robin and myself)
- who come from a functional programming background will find POP-11's
- partiall application order counter-intuitive. However, since no
- functional language that I aware of permits partial instantiation of
- tupled functions, why should we expect our intuitions to be satisfied?
-
- In short, the analogy between POP-11's partial application and the
- kind of partial application we encounter in functional languages
- is flawed. There is no difference between application and partial
- application in the lambda calculus because they are the same. There
- must be a distinction between partial application and procedure
- application in POP-11 because they are profoundly different.
-
- The order of arguments in functions such as maplist is naturally
- different in ML and POP-11 _because_ their very types are different.
- Speaking most approximately ....
-
- In ML maplist: ('a -> 'b) -> 'a list -> 'b list
- POP-11 maplist: ('a -> 'b) * 'a list -> 'b list
-
- When you write the equivalent to ML's maplist in POP-11 the difference
- vanishes ....
-
- define ml_maplist( proc );
- maplist(% proc %)
- enddefine;
-
- Now ml_maplist takes the arguments in the expected order and partial
- application and application are the same thing.
-
-
- > However the lambda calculus lambda v1...vn.E is really shorthand for
- > lambda v1 lambda v2....E,
- > so that the v1 is the first variable to be bound in a beta-reduction, and
- > hence should be taken off the top of the stack. POP does it the other way.
-
- Again, this pursues the weak analogy for POP. Much better would be add the
- following syntax to POP to fix up the problem.
-
- define ml_maplist( proc )( list ); ;;; allow multiple apps
- maplist( list, proc )
- enddefine;
-
- In fact, I'm all in favour of allowing any expression in the position
- of the procedure header that reduces to the application(s) of a variable
- to a sequence of variables. For example, you should be able to write
-
- define list.proc.ml_maplist;
- maplist( list, proc )
- enddefine;
-
- Not everyone will feel happy with this -- after all which is the
- variable name that is introduced with this syntax?
- list, proc, or ml_maplist
- It isn't as visually obvious as in today's POP syntax. But I feel
- the additional regularity is a worthwhile benefit.
-
-
- The introduction of this new procedure header syntax really only
- gets tricky when we consider dynamic-local formal parameters. But
- that's really very easy -- the binding to dynamic locals only occurs
- when the final parameter is bound in. In other words, the general
- translation of curried procedure definitions would introduce hidden
- lexical variables so you can write
-
- define foo( current_directory )( bill ); dlocal current_directory;
- ....
- enddefine;
-
- and have the current directory set to the frozen value when the
- inner procedure is applied to the second parameter. In other words
- this would get converted into ...
-
- define foo( v1 ); lvars v1;
- procedure( bill );
- dlocal current_directory = v1;
- ....
- endprocedure
- enddefine;
-
-
- Steve
-
-