guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Me no understand scoping


From: Andy Wingo
Subject: Re: Me no understand scoping
Date: Sat, 09 Aug 2008 13:05:58 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.0.60 (gnu/linux)

Hi Maciek,

On Sat 02 Aug 2008 23:36, "Maciek Godek" <address@hidden> writes:

> [Neil]
>> 1. IMO this could be really beautifully done in GOOPS, by defining
>> custom metaclasses and slot types.
>
> I've been considering that, and I'm still having doubts.
> The main reason is that there's no documented way
> of accessing GOOPS objects from C (except from using
> scm_c_eval_string etc.), or at least I couldn't find any
> documentation for that.

You can use scm_slot_ref et al. See goops.h.

> Besides (which is the matter of personal taste), I don't
> like the idea of using generics and trashing the global
> namespace with them. (I mean, the sole idea of generics
> is fine, but I wouldn't want to be forced to use them)
[...]
> I'm really trying to get close
> to the classical OOP notation: object.method() -- and
> it's probably why I explore the potential of using these
> "poor man's objects"

I wrote about this notational issue a while back on my blog,
http://wingolog.org/; I'm offline at the moment, so unfortunately I
don't have a link. Search for "slot-ref". I think with-accessors is an
elegant solution to this problem.

> But the point is that I saw that there is a 'make-hash-table' function
> available in lisp -- and this lead me to the conclusion that it's probably
> because the scopes/closures/environments implicitly use hash
> tables to store their bindings (and the same mechanism was given
> explicitly to the programmer).

This is false. Consider the closure:

  (let ((val 0))
    (lambda ()
      (set! val (1+ val))
      val))

Lexical scoping + closures was one of the fundamental ideas of scheme.
You can analyze this code /lexically/ to determine that we only need to
allocate storage for one variable. Wherever you see `val' in the body of
the lambda, you know /lexically/ that you are referring to a location
that is one step out in the stack frame, and the 0th location in that
frame, the frame created by `let'. So there is no need to store the
name, "val", at all!

By way of illustration, look at this disassembly of that closure in
guile-vm:

    scheme@(guile-user)> (let ((val 0)) (lambda () (set! val (1+ val)) val))
    $1 = #<program b6fbf388>
    scheme@(guile-user)> ,x $1
    Disassembly of #<program b6fbf388>:

    nargs = 0  nrest = 0  nlocs = 0  nexts = 0

    Bytecode:

       0    (late-variable-ref 0)
       2    (external-ref 0)
       4    (call 1)
       6    (external-set 0)
       8    (external-ref 0)
      10    (return)

    Objects:

       0    ((guile-user) . 1+)

    Externals:

       0    0

    Sources:

       6    #(1 36 #f)
       8    #(1 26 #f)

The external shown there, "0 0" refers exactly to the outer stack frame,
the zeroeth location. The external-ref and external-set instructions
manipulate that storage location /by index and not by name/.

Do you see now why local-eval can't possibly work in the presence of
efficient compilation? Scheme does not give you the particular kind of
dynamism that you want. There is no hash table lurking inside a closure.

On the other hand, modules do have hash tables, and modules are
evaluation environments; and they have been treated in some literature
as closures. Perhaps consider using modules as your first-class objects?

> And so I never stopped to believe that (define x 5) is more or
> less equivalent to (hash-set! global-scope 'x 5).

At the top level it is; but s/global-scope/the current module/.
But internal defines are completely different.

Happy hacking,

Andy
-- 
http://wingolog.org/




reply via email to

[Prev in Thread] Current Thread [Next in Thread]