Subject: Re: Anaphoric macros and packages
From: Erik Naggum <erik@naggum.no>
Date: 04 Dec 2002 23:37:10 +0000
Newsgroups: comp.lang.lisp
Message-ID: <3248033830373991@naggum.no>

* Gabor Melis
| What did I do wrong?

  You used a poorly designed feature.

  The key here is to get a short-hand for

(let ((expr (whatever)))
  (when expr
    (something-using-expr)))

  but in the tradition of really badly designed languages, an implicit
  binding is used instead of doing explicit bindings in the Common Lisp
  tradition, which would also allow more than one binding/expression.

  I would much prefer a syntax like this:

(whereas ((expr-1 (whatever-1))
          (expr-2 (whatever-2)))
  (something-using-expr-1-and/or-expr-2))

  Reading it like the standard contract legalese, the key idea is that all
  prior forms are true when an expression in the binding forms is evaluated
  (just like the operator `and´) and prior variable are bound to the (true)
  value of each prior expression.  The body is an implicit `progn´ that can
  thus rely on all of these forms having non-nil values.

  Having exhausted all useful things to do, I, too, have been thinking about
  how to do bindings and declarations more conveniently, and, not being the
  least bit afraid of parentheses, have decided on binding forms that have
  the general structure

(var-list [expression [decl-list]])

  where var-list is a designator for a list of (symbols naming) variables,
  expression may return multiple values bound to those variables with the
  standard nil default, and decl-list is a designator for a list of types.
  To be gratuitously super-clever, if the type of a variable in `whereas´
  is an explicit union of `null´ and something else to signal that `nil´ is
  a valid value, the result of the test is that using the succeeding value
  (etc by induction) and if it is the last value, the entire form is used
  for bindings, only.  Some examples may yet have a slightly illuminating
  effect:

(whereas (((value present-p) (gethash ...) ((or null ...))))
  ;; value is now actually obtained from the hashtable
  ...)
= (multiple-value-bind (value present-p) (gethash ...)
     (declare (type (or null ...) value))
     (when present-p
       ...)))

(whereas ((index (position ...) fixnum))
  ;; index now holds a fixnum index of an element actually in the sequence
  ...)
= (let ((index (position ...)))
    (when index
      (locally (declare (fixnum index))
        ...)))

(whereas ((cell (assoc ...) cons)
          (value (cdr cell)))
  ;; cell now holds an actually matching cons cell from the alist
  )
= (let ((cell (assoc ...)))
    (when cell
      (locally (declare (cons cell))
        (let ((value (cdr cell)))
          (when value
            ...)))))

  My `let´ and other binding forms parallel this development with a final
  (additional) argument that is the declared types, and allow a list of
  variables for binding multiple values.  Required arguments in lambda
  lists may be typed just like methods on generic functions.  The amount of
  effort to get this done is surprisingly small, and making it fully
  backward-compatible is almost easier than breaking things.

  Please remember that the purpose of this stunt is to give people a good
  reason to stay away from ill-designed and very un-Common-Lispy ways of
  "improving" on the language.  Macros that bind things and which neither
  work when nested nor when naïvely exported in the package system may
  appear "cool" to people who come from other languages, but not to the
  seasoned users.  Just like some may believe that "then" and "else" have a
  place in `if´ "statements" because the language they /really/ want to use
  does that, misguided attempts annoy people more than benefit a community.
  
-- 
Erik Naggum, Oslo, Norway

Act from reason, and failure makes you rethink and study harder.
Act from faith, and failure makes you blame someone and push harder.