From: Matthew D Swank
Subject: Questions about AMOP
Date: 
Message-ID: <pan.2006.06.09.03.12.43.607318@c.net>
I'm just starting Art of the Metaobject Protocol, and I have a couple very
basic (brain dead?) questions about Closette.

The book describes Closette as a metacircular interpreter.  What exactly
is being interpreted, and what is the evaluator?    

I'm used to seeing a function like eval: sexp -> value:

(eval '((lambda (x y) (+ x y)) 3 4))

-> 7

Or, if we don't want to bother with a concrete syntax:

(eval-expr-obj (make-application (make-lam (list 'x 'y) 
                                   (make-sum (make-var 'x) (make-var 'y)))
                                 (list (make-num 3) (make-num 4)))
               null-env)
-> 7

What is the evaluation mechanism in Closette, and how is the machinery
defined in part one different from a "compiled" CLOS?

Another thing that confuses me is omitting the bootstrapping code from the
implementation until the appendices.  This makes apparent circularities
look like real circularities, and confuses (in my mind) the implementation
language.  Is the code in part one meant to be taken as Closette defined
in CLOS, or Closette defined in Closette?

Matt
(scratching his head)

-- 
"You do not really understand something unless you can
 explain it to your grandmother." — Albert Einstein.

From: Pascal Costanza
Subject: Re: Questions about AMOP
Date: 
Message-ID: <4esn0cF1ef3jfU1@individual.net>
Matthew D Swank wrote:
> I'm just starting Art of the Metaobject Protocol, and I have a couple very
> basic (brain dead?) questions about Closette.
> 
> The book describes Closette as a metacircular interpreter.  What exactly
> is being interpreted, and what is the evaluator?    

Closette is an object system implemented in itself, i.e., Closette, just 
like Lisp is a symbolic language implemented in itself, i.e., Lisp.

It is an interpreter because the class, generic function, method 
definitions, etc., are not translated into an underlying language, but 
are represented as data (metaclass instances) that are interpreted by 
the generic functions of the implementation level.

Unlike in a metacircular implementation of Lisp itself, where programs 
are represented as s-expressions, Closette programs are represented as 
objects. (CLOS is basically no different from this.)

> What is the evaluation mechanism in Closette, and how is the machinery
> defined in part one different from a "compiled" CLOS?

I don't have the book at hand, but the rough idea is that Closette 
defines apply-generic-function which takes a generic function and some 
parameters, inspects the classes of the parameters, selects the 
applicable methods and executes the applicable methods in the correct 
order. That's like eval which takes an s-expression, inspects the car 
position, selects the correct meaning and executes it.

In the CLOS MOP, there is a step taken before apply-generic-function, 
i.e., compute-discriminating-function, which allows compilation 
(actually partial evaluation) of that process. But the idea is basically 
the same.

> Another thing that confuses me is omitting the bootstrapping code from the
> implementation until the appendices.  This makes apparent circularities
> look like real circularities, and confuses (in my mind) the implementation
> language.  Is the code in part one meant to be taken as Closette defined
> in CLOS, or Closette defined in Closette?

It's always X implemented in X, that is something implemented in itself.

Note that when a metacircular interpreter for Lisp/Scheme is presented, 
you typically also don't get to see the bootstrapping code - typically 
even not at all.


Pascal

-- 
3rd European Lisp Workshop
July 3 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/
From: Matthew D Swank
Subject: Re: Questions about AMOP
Date: 
Message-ID: <pan.2006.06.10.03.30.55.637507@c.net>
On Fri, 09 Jun 2006 09:40:59 +0200, Pascal Costanza wrote:


> It's always X implemented in X, that is something implemented in itself.
> 

Well there is a little wiggle room here since the Closette features used
in the implementation are a subset of the features available in CLOS...

> Note that when a metacircular interpreter for Lisp/Scheme is presented,
> you typically also don't get to see the bootstrapping code - typically
> even not at all.

Well, unless you count the snarfing of certain implementation features as
primitives in the interpretor (arithmetic operators, the reader, etc.),
there aren't as many bootstrapping issues in your garden variety
metacircular lisp interpreter.  There is no procedural reflection for one
thing.
 
Btw thanks for the clarification,

Matt
-- 
"You do not really understand something unless you can 
explain it to your grandmother." — Albert Einstein.
From: Matthew D Swank
Subject: Re: Questions about AMOP
Date: 
Message-ID: <pan.2006.06.10.03.41.15.532785@c.net>
On Fri, 09 Jun 2006 09:40:59 +0200, Pascal Costanza wrote:

> Closette defines apply-generic-function which takes a generic function and 
> some parameters, inspects the classes of the parameters, selects the 
> applicable methods and executes the applicable methods in the correct 
> order. That's like eval which takes an s-expression, inspects the car 
> position, selects the correct meaning and executes it.

In languages where methods and generic functions are primitives, how are
anonymous functions usually represented?  Are methods made funcallable, or
do they belong to some degenerate generic-function that has a trivial
dispatch mechanism (apply the method-body!)?

I suppose one could even have a non generic-function subclass of
the whatever the equivalent of funcallable-instance would be in the target
language.

Matt

-- 
"You do not really understand something unless you can
 explain it to your grandmother." — Albert Einstein.
From: Kent M Pitman
Subject: Re: Questions about AMOP
Date: 
Message-ID: <u3bedwsgq.fsf@nhplace.com>
Matthew D Swank <·······································@c.net> writes:

> On Fri, 09 Jun 2006 09:40:59 +0200, Pascal Costanza wrote:
> 
> > Closette defines apply-generic-function which takes a generic function and 
> > some parameters, inspects the classes of the parameters, selects the 
> > applicable methods and executes the applicable methods in the correct 
> > order. That's like eval which takes an s-expression, inspects the car 
> > position, selects the correct meaning and executes it.
> 
> In languages where methods and generic functions are primitives, how are
> anonymous functions usually represented?  Are methods made funcallable, or
> do they belong to some degenerate generic-function that has a trivial
> dispatch mechanism (apply the method-body!)?

In original-Dylan (the one Apple published about in 1992 that had Lispy
syntax before it was given what I'll euphemistically call a 
"syntactic facelift"), methods are presented syntactically on par with
functions, and are able to take type qualifiers.  On p30, before you get
to define (p31), define-generic-function (p39), and define-method (p40), 
you get to "anonymous methods", with
  (method ((a <integer>) (b <integer>))
    (list (- a b) (+ a b)))
presented as an example.


In T (Yale Scheme), in which Jonathan Rees and I did most of the original
design, LAMBDA was primitive and we layered a more complex concept on it
which was a special form looking something like:
  (OBJECT (LAMBDA (param-0-1 param-0-2 ...) 
             ...distinguished method to handle funcalls...)
    (method-key-1 (param-1-1 param-1-2 ...) ... handler for method ...)
    (method-key-2 (param-2-1 param-2-2 ...) ... handler for method ...)
    ...
    (=> parent-handler))
e.g.,

 (define (make-cell cell-value)
   (object (lambda () cell-value)
     ((setter self)
      (lambda (new-value) (set cell-value new-value)))
     ((print self stream)
      (format stream "#<cell value=~S>" cell-value))))

where the clauses were just syntax for additional lambdas, of course, 
as if you'd done

 (define (make-cell cell-value)
   (internal-make-object 
     (list (list funcall (lambda () cell-value))
           (list setter  (lambda (new-value) (set cell new-value)))
           (list print   (lambda (self stream) (format ...))))))

> I suppose one could even have a non generic-function subclass of
> the whatever the equivalent of funcallable-instance would be in the target
> language.

It can be made to work either way.  Remember that how it's implemented
doesn't have to be how it's presented to the user.   e.g., Dylan probably
had something more primitive, but it didn't like exposing that to users.
Some CL implementations expose the difference between a primitive function
and a closure to users, but some don't.

Paul Robertson (architect of the implementation that started as RCL
[Robertson Common Lisp] and later was bought by Symbolics to become
CLOE, a native 386-based Common Lisp for application deployment) once
argued to me that it was a misdesign in Common Lisp that it doesn't
expose the difference between functions and closures, and that people
who are cons-conscious would trust the language more if it had an 
operator that was guaranteed to not cons (but would err rather than
capturing a free variable) and a different one that might cons (to
highlight the capture of free variables).  This is probably as offensive
to functional language people as a Lisp1 is, but I think he had a good
point.  To we who use closures a lot, and who trust the GC, we like to
slip fluidly between the two styles without renaming the operator.  But
there are people who either don't use Lisp or use it only under strict
rules of cons-avoidance who might really appreciate operators that were
guaranteed not to capture, as in:
  (proc (x y) (+ x y)) ;works ok
  (proc (x y) (+ x y z)) ; NOT ok because z is free
In this way, it would be no runtime overhead to make use this function.
While
  (closure (x y) (+ x y z)) 
would alert the programmer visually to what some might think an unusual
situation, that is, that a free variable is being captured.
From: Matthew D Swank
Subject: Re: Questions about AMOP
Date: 
Message-ID: <pan.2006.06.10.08.02.50.851162@c.net>
On Sat, 10 Jun 2006 04:19:11 +0000, Kent M Pitman wrote:

> Paul Robertson 
..
> once argued to me that it was a misdesign in Common Lisp that it doesn't
> expose the difference between functions and closures, and that people
> who are cons-conscious would trust the language more if it had an 
> operator that was guaranteed to not cons (but would err rather than
> capturing a free variable) and a different one that might cons (to
> highlight the capture of free variables).  This is probably as offensive
> to functional language people as a Lisp1 is, but I think he had a good
> point.  To we who use closures a lot, and who trust the GC, we like to
> slip fluidly between the two styles without renaming the operator.  But
> there are people who either don't use Lisp or use it only under strict
> rules of cons-avoidance who might really appreciate operators that were
> guaranteed not to capture, as in:
>   (proc (x y) (+ x y)) ;works ok
>   (proc (x y) (+ x y z)) ; NOT ok because z is free
> In this way, it would be no runtime overhead to make use this function.
> While
>   (closure (x y) (+ x y z)) 
> would alert the programmer visually to what some might think an unusual
> situation, that is, that a free variable is being captured.

Though it doesn't provide the syntactic queues above, it would seem a
less violent alteration of the language to simply provide 'proc and
'closure as "sanctioned" subtypes of 'function, and make them available
for declarations and compiler warnings.  Then, provide some magic
incantation about how functions w/o free vars will be of type 'proc, they
won't cons, and you can go on pretending you're programming in Algol.

Not that anything in Lisp gets standardized anymore.

Matt 

-- 
"You do not really understand something unless you can
 explain it to your grandmother." — Albert Einstein.
From: Matthew D Swank
Subject: Re: Questions about AMOP
Date: 
Message-ID: <pan.2006.06.10.13.15.36.138012@c.net>
On Sat, 10 Jun 2006 03:02:52 -0500, Matthew D Swank wrote:

> queues
:s/queues/cues

-- 
"You do not really understand something unless you can
 explain it to your grandmother." — Albert Einstein.