[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: 2016-05-23 Emacs News
From: |
Nicolas Richard |
Subject: |
Re: 2016-05-23 Emacs News |
Date: |
Fri, 10 Jun 2016 16:55:45 +0200 |
User-agent: |
mu4e 0.9.17; emacs 25.0.92.1 |
Rolf Ade <address@hidden> writes:
> Is what the article demonstrates something special to the 'build-in'
> function sort or to emacs lisp? It would help me, if someone explains
> what happen in this example in other words (not in implementation detail
> but language concepts).
Note : meanwhile I saw Marcin posted a much shorter re-explanation... oh
well, I'll just post this anyway :)
I can't really do it in language-concepts because I don't know which
concepts you're familiar with, and more importantly because *I* am not
familiar enough with the concepts. I still hope it will be helpful :
It's related to two facts:
(i) sort changes the list inplace.
(ii) the list is built at read-time instead of call-time, so that
calling the same function multiple times re-uses the same list.
Now the steps that show the "problem". I'll try to explain very slowly.
I hope this is not too much details.
A) lisp code is actually a lisp list. In our example, the code that
defines destructive-havoc is this :
(defun destructive-havoc ()
"Example of destructive havoc."
(setq foo '(1 3 2))
(message "before sort, foo is: %s" foo)
(sort foo #'<)
(message "after sort, foo is: %s" foo))
That clearly looks like a list (first element would be the symbol
`defun`, then the symbol `destructive`, then the empty list aka symbol
`nil`, then a string, then four more lists). When you want to eval this
code, emacs will first turn this list-looking-thing-in-your-buffer into
an actual Lisp list. This is the reading step, aka read-time.
Two more things to note in this step :
- when emacs read '(1 3 2), it turned it into (quote (1 3 2)). This is
what ' means. So this is in fact a list made of the symbol quote and
the list (1 3 2). This list (1 3 2), I'll call it FooBar.
- if you read the docstring of `quote`, it mentions something about «
the value that was pre-constructed by the Lisp reader » and what it
refers to, is exactly the FooBar list. But quote is not doing its
job right now, so I'll come back to that later.
B) Since we asked, emacs will now proceed to evaluating this big list.
This is what I called "call-time".
How it does it is described in (info "(elisp) Intro Eval"), but
basically it first looks at the first element of the list, and then
decides what to do with the rest of the list. In this case the first
element is `defun`, which is a symbol which denotes a macro, thus emacs
will expand the macro and do its thing. At the end of this step,
emacs now turned the symbol `destructive-havoc` into a function which
holds the following list as its definition:
(lambda nil "Example of destructive havoc." (setq foo (quote (1 3 2)))
(message "before sort, foo is: %s" foo) (sort foo (function <)) (message "after
sort, foo is: %s" foo))
Here, note that a part of this list is in fact the bit that was created in
step
A, namely everything from (setq ... until the last paren. In
particular, the (1 3 2) list in there is our FooBar.
C) next thing you'll be doing is evaluate (destructive-havoc). Again,
emacs will read first (like step A) then evaluate (like step B) this
code. This time, when evaluating, emacs sees destructive-havoc is symbol
with a function definition, which means it'll evaluate the list
contained in that function, i.e. the (lambda ...) form we have mentionned
previously.
Evaluating this lambda essentially means to evaluate all elements of the
code inside, which is those :
(setq foo (quote (1 3 2)))
(message "before sort, foo is: %s" foo)
(sort foo (function <))
(message "after sort, foo is: %s" foo)
C1) The first thing is the setq, which gives `foo' the value obtained from
evaluating (quote (1 3 2)). At this point, evaluating (quote (1 3 2))
returns the value pre-constructed by the lisp reader in step A at the
very beginning, which is FooBar.
C2) message constructs a string and shows it in the *Messages* buffer.
C3) sort sorts in-place, thus changing the data. IOW, FooBar was (1 3 2)
but is now (1 2 3). It's the same list, but its content was changed.
C4) message constructs a string and shows it in the *Messages* buffer.
*at this point in time*, since the function definition contained FooBar
and FooBar has had its content changed, the function inside
destructive-havoc has somehow... changed too. It didn't change, really,
but its content changed.
D) if you re-evaluate (destructive-havoc), the same steps happen again,
but since FooBar is now (1 2 3), what you see at step D2 is different.
Please note that despite the rather long post, I left out some details,
in particular (i) the difference between macro (or special form) and
function, (ii) the actual implementation of lists (as cons cells), (iii)
the byte-compilation and eager macro-expansion that might actually
happen when you evaluate things manually, even though you did not
request them.
HTH anyway.
--
Nicolas