home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #31 / NN_1992_31.iso / spool / comp / lang / pop / 168 < prev    next >
Encoding:
Internet Message Format  |  1992-12-22  |  12.1 KB

  1. From: sfk@otter.hpl.hp.com (Steve Knight)
  2. Date: Sun, 20 Dec 1992 15:00:33 GMT
  3. Subject: Re: POP syntax
  4. Message-ID: <116670036@otter.hpl.hp.com>
  5. Organization: Hewlett-Packard Laboratories, Bristol, UK.
  6. Path: sparky!uunet!zaphod.mps.ohio-state.edu!sdd.hp.com!hpscit.sc.hp.com!hplextra!otter.hpl.hp.com!otter!sfk
  7. Newsgroups: comp.lang.pop
  8. References: <57553@dime.cs.umass.edu>
  9. Lines: 285
  10.  
  11. Here's a lengthy response to Robin's provocative and interesting
  12. message.  Being a response, it moves around several topics which
  13. should probably be in distinct messages.  However, there is a common
  14. thread of possible revisions to POP and revisiting old design 
  15. decisions that makes them hang together is quite an intriguing way,
  16. I believe .....
  17.  
  18. Robin Popplestone writes about POP's list syntax:
  19. >  Probably if we had
  20. > had a supply of curlies in the early days we would have used {this is a
  21. > list} as being somehow suitable for lists as set-like things (and perhaps
  22. > we would have been wrong - certainly it is no help to think of an
  23. > application as a set!, [text deleted]
  24.  
  25. It is interesting to see that GLOW took the route of using braces 
  26. and brackets to distinguish quoted and unquoted lists.  It seems that
  27. POP language designers are keen to be able to conveniently denote 
  28. nested list constants.  My own view is similar to Robin's -- it is waste
  29. of precious bracketing symbols and not mnemonic to use braces in this
  30. way.
  31.  
  32. I was fascinated to watch Chris Dollin, the designer of Pepper, slowly and 
  33. reluctantly accepting the need for list quasi-quoting.  His solution was
  34. to integrate the list and vector syntax into the single quote context.
  35. So in Pepper, you can write "[ ... ]" to achieve the same effect as POP-11's
  36. [ ... ] and, naturally enough, "{ ... }" to do the same as POP-11's { ... }.
  37. (There are minor semantic differences discussed below.)
  38.  
  39. The result in Pepper is that there is only one quoting system which is
  40. introduced by the quote symbol '"'.  The disadvantage is that the quote
  41. is really a quasi-quote and more complex to learn -- although not as hard
  42. as POP-11's list syntax since it has a more regular semantics and a single
  43. escape mechanism.
  44.  
  45. One of the most powerful arguments for the kind of solution Pepper has
  46. pioneered is the rationalisation of quoting semantics.  Here's a question
  47. for all you budding POP-11 experts out there (yes, I'm talking to you
  48. James) that illustrates the needless complexity of its quoting
  49. semantics.  Try to answer without referring to any manuals and without
  50. typing the answer to a POP-11 interpreter ...
  51.  
  52. What are A, B, C, and D bound to after the following statements are executed?
  53.     vars A, B, C, D;
  54.     vars L = [a b c];
  55.     vars V = {a b c};
  56.     {1 ^^V 2} -> A;
  57.     [1 ^^L 2] -> B;
  58.     {1 ^^L 2} -> C;
  59.     [1 ^^V 2} -> D;
  60.  
  61. And if you got that right, try this one:
  62.     vars L = conspair( "a", "b" );
  63.     [1 ^^L] -> A;
  64.     [1 ^^L 2] -> B;
  65.  
  66. As I alluded to above, the separation of quoting from list and vector
  67. construction, as in Pepper, makes the above irregularities vanish.  Since
  68. the semantics of the anti-quotation operator '^^' are defined with 
  69. respect to quotation rather than list or vector construction. 
  70.  
  71.  
  72. Robin then moves onto a few of his least favourite things:
  73. > My pet HATE is having to declare my lexicals as lvars. I have from time to
  74. > time tried to do little hacks to avoid this, but have not got a stable one.
  75. > Also, while we are at it, the control variable of a for loop should be
  76. > automatically local to the loop. All these things are being incorporated in
  77. > Pepper.
  78.  
  79. I completely agree.  I think that Scheme shows the correct way to do
  80. loop variables -- it makes them local to the loop and the loop-start
  81. is outside of that local context.  As a consequence, each time around the
  82. loop, the control variable is declared.  This means that simple loops 
  83. such as 
  84.     for i in [a b c] do
  85.         procedure(); i endprocedure
  86.     endfor
  87. would deliver 3 separate procedures instead of just one.
  88.  
  89.  
  90. But then Robin starts to edge off the rails by complimenting ML:
  91. > Another thing I would like is a neat, concise syntax for anonymous
  92. > functions. The old LAMBDA .......END construct was painful enough, without
  93. > being replaced by procedure.....endprocedure. ML's  fn <patt> => <body> is
  94. > neat.
  95.  
  96. It might well be nice to have a concise pair of procedure brackets (and I
  97. tend to use fun ... endfun) but let's not have the ML model.  ML makes 
  98. several dire misdesign moves that makes the kind of mistakes I have been
  99. discussing in POP-11 look like models of excellent design.  The "fn" 
  100. syntax is a prime example.  Suffice it to say that there are two serious
  101. blunders in the design of the anonymous function syntax in ML which
  102. are (1) the lack of a closing keyword (2) using the same pattern continuation
  103. symbol (the vertical bar '|') as the "case" and "fun" constructs where
  104. anonymous functions frequently appear.  The result of this is that it
  105. is usual to write 
  106.     (fn <patt> => <expr>)
  107. in order to prevent unwanted ambiguity from creeping in.  Come back LISP,
  108. all is forgiven!
  109.  
  110. And I would also like to see the "anonymous" procedure syntax extended to
  111. allow the addition of a name for the procedure.  e.g.
  112.     procedure fact( n );
  113.         if n <= 1 then 1 else n * fact( n - 1 ) endif
  114.     endprocedure
  115. And this wouldn't merely be describing the -pdprops- of the procedure.  The
  116. intention would be that the procedure would be able to refer to itself by
  117. that label.  
  118.  
  119. What is the point?  Well, it means that the definition of the factorial
  120. function given above is immune from the assignment to "fact".  
  121. Normally writing directly recursive functions means that they are
  122. vulnerable to manipulation of the top-level identifer.  This device
  123. ensures that they keep their meaning.  And this
  124. property means that directly recursive functions can often be significantly
  125. optimised.  This is one of those lovely additions that is completely in
  126. harmony with the language and benefits all users, direct and indirect.
  127.  
  128. [For compiler writers, note that this means that tail-call optimisation
  129. is safe.]
  130.  
  131.  
  132. Finally, Robin moves onto a subject which he clearly worries about
  133. a great deal -- the disparity between POP-11 and functional languages
  134. in the way they effect partial application:
  135. > An interesting point is whether there is any way of getting rid of the
  136. > distinction between application and partial application, which is not a
  137. > fundamental distinction of the lambda calculus, but is only (there) an
  138. > implementation issue. 
  139.  
  140. I don't think this is the right way of looking at POP-11 at all.  The
  141. proper comparison is that a multi-argument POP procedure is like a 
  142. function with a tupled argument.  POP's partial application freezes
  143. in the first element of the tuple yielding a function whose domain
  144. is one type shorter. 
  145.  
  146. To illustrate this, let's write a trinary function in both POP-11 and
  147. an imaginary version of ML that dynamically typechecks.
  148.  
  149.     define foo( z, y, x ); <EXPR> enddefine;
  150.  
  151.     fun foo( x, y, z ) = <EXPR>;
  152.  
  153. Now we specialise on the argument x, with x = X.
  154.  
  155.     vars fooX = foo(% X %);
  156.  
  157.     val fooX = 
  158.         let fun freeze( frozval, func ) tuple =
  159.                     let val L = tuple_to_list tuple;
  160.                         val T = list_to_tuple( frozval :: L )
  161.                     in
  162.                         func T
  163.                     end;
  164.         in
  165.             freeze( X, foo )
  166.         end;
  167.  
  168. Now we can see the real advantage of partial application!  There's no
  169. equivalent in strongly typed functional languages because tuples are
  170. not abstracted over in the type system.  But in a dynamically typed
  171. system, such as POP, the advantages are clear.  
  172.  
  173. In a language like ML, there is always a tension between defining a 
  174. function in curried form or tupled form.  This is because the ML
  175. syntax is geared up to deal more conveniently with tupled functions
  176. but curried functions work with partial application more elegantly.
  177.  
  178. But in a dynamically typed language such as POP-11, this tension
  179. disappears because we can write our functions in tupled form *and*
  180. partially apply them.  So it is very uncommon to see code such as 
  181. this
  182.     define foo( z );
  183.         procedure( y );
  184.             procedure( x );
  185.                 <EXPR>
  186.             endprocedure
  187.         endprocedure
  188.     enddefine;
  189.  
  190. The closest one gets to writing this is when you wish to partially
  191. evaluate on the basis of the frozen argument.  An example of this
  192. might be the form of a regular expression matcher.  One would typically
  193. write
  194.     define re_match( regexp );
  195.         lvars fast_matcher = re_compile( regexp );
  196.         procedure( target );
  197.          .... fast_matcher( target ) ...
  198.         endprocedure
  199.     enddefine;
  200.  
  201.  
  202.  
  203. > A related problem is that partial application
  204. > in POP-11 "works backwards", as compared with the lambda calculus (and
  205. > therefore ML and Haskell). This is a real problem in mixed language
  206. > working, because there are actually some arguments that are more usefully
  207. > partially applied than others. E.g. we (Burstall and I) chose the order of
  208. > arguments in maplist so that the function argument could be bound by
  209. > partial application.  The order is OPPOSITE in ML.
  210.  
  211. If one accepts my comparison between partial application and tuple
  212. instantiation, then this is plainly the wrong comparison.  The strongest
  213. statement that you can make is that people (such as Robin and myself)
  214. who come from a functional programming background will find POP-11's
  215. partiall application order counter-intuitive.  However, since no
  216. functional language that I aware of permits partial instantiation of
  217. tupled functions, why should we expect our intuitions to be satisfied?
  218.  
  219. In short, the analogy between POP-11's partial application and the
  220. kind of partial application we encounter in functional languages
  221. is flawed.  There is no difference between application and partial
  222. application in the lambda calculus because they are the same.  There
  223. must be a distinction between partial application and procedure
  224. application in POP-11 because they are profoundly different.
  225.  
  226. The order of arguments in functions such as maplist is naturally 
  227. different in ML and POP-11 _because_ their very types are different.
  228. Speaking most approximately ....
  229.  
  230. In ML    maplist: ('a -> 'b) -> 'a list -> 'b list
  231. POP-11   maplist: ('a -> 'b) * 'a list -> 'b list
  232.  
  233. When you write the equivalent to ML's maplist in POP-11 the difference
  234. vanishes ....
  235.  
  236.     define ml_maplist( proc ); 
  237.         maplist(% proc %)
  238.     enddefine;
  239.  
  240. Now ml_maplist takes the arguments in the expected order and partial
  241. application and application are the same thing.  
  242.  
  243.  
  244. > However the lambda calculus  lambda v1...vn.E is really shorthand for
  245. >     lambda v1 lambda v2....E,
  246. > so that the v1 is the first variable to be bound in a beta-reduction, and
  247. > hence should be taken off the top of the stack. POP does it the other way.
  248.  
  249. Again, this pursues the weak analogy for POP.  Much better would be add the 
  250. following syntax to POP to fix up the problem.
  251.  
  252.     define ml_maplist( proc )( list );  ;;; allow multiple apps
  253.         maplist( list, proc )
  254.     enddefine;
  255.  
  256. In fact, I'm all in favour of allowing any expression in the position
  257. of the procedure header that reduces to the application(s) of a variable
  258. to a sequence of variables.  For example, you should be able to write
  259.  
  260.     define list.proc.ml_maplist;
  261.         maplist( list, proc )
  262.     enddefine;
  263.  
  264. Not everyone will feel happy with this -- after all which is the
  265. variable name that is introduced with this syntax?  
  266.     list, proc, or ml_maplist
  267. It isn't as visually obvious as in today's POP syntax.  But I feel 
  268. the additional regularity is a worthwhile benefit.
  269.  
  270.  
  271. The introduction of this new procedure header syntax really only
  272. gets tricky when we consider dynamic-local formal parameters.  But
  273. that's really very easy -- the binding to dynamic locals only occurs 
  274. when the final parameter is bound in.  In other words, the general
  275. translation of curried procedure definitions would introduce hidden
  276. lexical variables so you can write
  277.  
  278.     define foo( current_directory )( bill ); dlocal current_directory;
  279.         ....
  280.     enddefine;
  281.  
  282. and have the current directory set to the frozen value when the 
  283. inner procedure is applied to the second parameter.  In other words
  284. this would get converted into ...
  285.  
  286.     define foo( v1 ); lvars v1;
  287.         procedure( bill );
  288.             dlocal current_directory = v1;
  289.             ....
  290.         endprocedure
  291.     enddefine;
  292.  
  293.  
  294. Steve
  295.  
  296.