Subject: Re: Macros OR, AND
From: Erik Naggum <erik@naggum.net>
Date: Sun, 18 Nov 2001 03:07:11 GMT
Newsgroups: comp.lang.lisp
Message-ID: <3215041628177349@naggum.net>

* Thomas F. Burdick
| But this is exactly the weird case!  expensive-general-case can return
| multiple values, but cached-result can't!

  Oh, but I used my own definition of the macro or that solves this
  problem, and you SHOULD NOT use the braindamaged standard macro.  :)

  But seriously, I recognize your problem.  It might actually be a good
  idea to specify or and cond to be multivalued -- it would not take a huge
  effort to provide compiler support for this, but there is a little more
  effort involved when interpreted.  However, one of the problems with
  multivalued functions is that the caller context knows whether it will
  return single or multiple values, but nobody is listening.  That is, if a
  function may be multivalued, it should be able to skimp on the cost of
  preparing for returning multiple values if only the primary value is
  used.  This might, understandably, have created a reluctance to use
  multiple values by default.

  The "problem" of communicating the expectation of multiple values has
  some similarities with the annoying tail-call problem, but fortunately, a
  compiler or interpreter will have to know how to set up its multivalue
  return mechanism or pass through whatever the value-returning form of the
  function returned, so the point where this information would both be
  passed _and_ understood is already known.  However, there is no language
  support for this knowledge, so there is no way to utilize that knowledge.
  E.g., cond would need to know whether the "calling" form was about to
  return its value or not to make this painless and transparent insead of
  costly and cumbersome.

  The typical implementation of the macro or is to use the feature of the
  macro cond that it return the value of the test-form if there are no body
  forms, but it is specified to return only the primary value in that case.
  To amend this would require some work.  If a cond form transform into an
  if form like this:

(cond (test-form) ...)
-> (let ((<gensym> test-form))
     (if <gensym>
       <gensym>
       (cond ...)))

  It would have to be something like this

-> (let ((<gensym> (multiple-value-list test-form)))
     (if (first <gensym>)
       <gensym>
       (cond ...)))

  except that the internal multivalue return mechanism would probably be
  used instead of going through the expensive list representation, such
  that it would not actually need to allocate anything in the caller.  (I
  am concerned with this only because the penalty for using multiple values
  should be so low that people would not have to think twice about it.)

  In order for this to _really_ work well, a new language feature would be
  very welcome.  Implementations already have to have _some_ means to
  return multiple values, so suppose that multiple values were represented
  by a first-class object instead of the sort of "transient" magic it is
  today.  The function values would create and return such an object.  If
  stored in a variable and later referenced, its value would be the primary
  value.  If referenced by the multivalue special operators, it would work
  just like it were the original call to values as we are used to it today
  in the form of storing the value of multiple-value-list and expanding it
  back to multiple values with (apply #'values <list>).

  The complicating situation here is that this new kind of first-class
  object needs to work transparently as if it were its primary value.
  (Back on the good old PDP-10 this kind of data-driven indirection would
  not have been a problem, but sadly, modern processors are so deficient.
  Then again, many things would not have been problems if the PDP-10 had
  been the model for modern processors.  You who knew the PDP-10, know what
  I mean.  The rest should know that this not mere nostalgia.)

  Note that this would make the original expansion of cond continue to work
  with multiple values.  It would also mean we would have an asymmetry that
  some might find quite disconcerting:

(let* ((foo (values 1 2 3))
       (bar foo))
  (list (eq foo bar) (multiple-value-list foo) (multiple-value-list bar)))
=> (t (1 2 3) (1))

| (I can understand not being able to name that macro, though.  I started
| naming things ...-maybe that would have had super long names, and I
| didn't like it at first, but I got used to it.  It's probably a bad name,
| but it's fine for my personal use :) )

  Perhas something along the line of intern?

| This is a case where I wonder if the flexibility of macros hasn't maybe
| prevented a more powerful notation from having developed, because we can
| fake it every specific time we need it.  Oh well.

  Well, we have symbol-macros, which can be really powerful, too, once
  people figure out how to use them.  Making the full use of existing
  features is quite hard, so it may be that implementing something on top
  of macros simply does not yield enough of an inconvenience to spawn
  serious interest in alternatives?  That is, not the flexibility of
  macros, but the lack of inconvenience in doing something with them.

///
-- 
  Norway is now run by a priest from the fundamentalist Christian People's
  Party, the fifth largest party representing one eighth of the electorate.
-- 
  Carrying a Swiss Army pocket knife in Oslo, Norway, is a criminal offense.