unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* return
@ 2010-11-26  8:57 Lars Magne Ingebrigtsen
  2010-11-26  9:19 ` return Tassilo Horn
                   ` (2 more replies)
  0 siblings, 3 replies; 31+ messages in thread
From: Lars Magne Ingebrigtsen @ 2010-11-26  8:57 UTC (permalink / raw)
  To: emacs-devel

Wouldn't it be nice if Emacs Lisp had a workable early-return mechanism?

(This is where Andreas chimes in with "What's wrong with <foo>?")

While debugging and fixing stuff, I often find myself in the situation
of adding more if/cond statements to a function and pushing the original
body further in.  I think this usually leads to less clear code.

Like with 

(defun foo ()
  ... lots of code)

the options are usually

(defun foo ()
  (if zot
      t
    ... lots of code))

or (the horror!)

(defun foo ()
  (block nil
    (when zot
       (return t))
    ... lots of code))

or

(defun foo ()
  (if zot
      t
    (foo-1)))

(defun foo-1 ()
  ... lots of code)

and so on.  I think the ideal way to deal with this is to be more C-ish,
and say

(defun foo ()
  (when zot
    (return t))
  ... lots of code)

That is, I think it would probably be pretty nice if every defun
establishes a nil block.  So this wouldn't be a Common Lisp-ism, but
better!  :-)

(If we want to avoid confusion with the CL return, then we could call it
something else, like freturn.)

This isn't the only use case, of course.  A lot of functions in Emacs go
through pretty awkward contortions to end loops when certain conditions
occur.  A simple return from the middle of a complex series of loops can
often make the code a lot more readable.  (And probably faster.)

Thoughts?
  
-- 
(domestic pets only, the antidote for overdose, milk.)
  larsi@gnus.org * Lars Magne Ingebrigtsen




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-26  8:57 return Lars Magne Ingebrigtsen
@ 2010-11-26  9:19 ` Tassilo Horn
  2010-12-04  2:36   ` return Fren Zeee
  2010-11-26  9:24 ` return Miles Bader
  2010-11-26 14:59 ` return Stefan Monnier
  2 siblings, 1 reply; 31+ messages in thread
From: Tassilo Horn @ 2010-11-26  9:19 UTC (permalink / raw)
  To: emacs-devel; +Cc: Lars Magne Ingebrigtsen

On Friday 26 November 2010 09:57:21 Lars Magne Ingebrigtsen wrote:
> Wouldn't it be nice if Emacs Lisp had a workable early-return
> mechanism?

I guess so.  One option you didn't list is using catch/throw:

(defun foo (n)
  (catch 'val
    (dostuff)
    (when zot
      (throw 'val t))))

That's at least not something you need the cl package for.

Bye,
Tassilo



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-26  8:57 return Lars Magne Ingebrigtsen
  2010-11-26  9:19 ` return Tassilo Horn
@ 2010-11-26  9:24 ` Miles Bader
  2010-11-26  9:36   ` return Lars Magne Ingebrigtsen
  2010-11-26  9:44   ` return Tassilo Horn
  2010-11-26 14:59 ` return Stefan Monnier
  2 siblings, 2 replies; 31+ messages in thread
From: Miles Bader @ 2010-11-26  9:24 UTC (permalink / raw)
  To: emacs-devel

Lars Magne Ingebrigtsen <larsi@gnus.org> writes:
> That is, I think it would probably be pretty nice if every defun
> establishes a nil block.  So this wouldn't be a Common Lisp-ism, but
> better!  :-)
>
> (If we want to avoid confusion with the CL return, then we could call it
> something else, like freturn.)

Doesn't every defun in CL establish a nil block?

What you're proposing looks pretty much exactly like CL...

(by "CL", note that I mean "Common Lisp", not "cl.el")

Which is fine of course, CL's block mechanism is nice.

Of course one question is:  what about returns out of lambdas?
Would/should they be supported, given that they need an entirely
different mechanism internally?

-Miles

-- 
"An atheist doesn't have to be someone who thinks he has a proof that
there can't be a god.  He only has to be someone who believes that the
evidence on the God question is at a similar level to the evidence on
the werewolf question."  [John McCarthy]



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-26  9:24 ` return Miles Bader
@ 2010-11-26  9:36   ` Lars Magne Ingebrigtsen
  2010-11-26  9:54     ` return Miles Bader
  2010-11-26  9:44   ` return Tassilo Horn
  1 sibling, 1 reply; 31+ messages in thread
From: Lars Magne Ingebrigtsen @ 2010-11-26  9:36 UTC (permalink / raw)
  To: emacs-devel

Miles Bader <miles@gnu.org> writes:

> Doesn't every defun in CL establish a nil block?

No, it establishes a named block, so you can say

(defun foo ()
  (when zot
    (return-from foo
      t))
  ... stuff)

Which is kinda annoying, in my opinion.  Especially if you have long
function names and a lot of return-from forms.  Or you rename function
names...

But that's also an option if we think it's better not to confuse Common
Lisp peeps. 
  
> Of course one question is:  what about returns out of lambdas?
> Would/should they be supported, given that they need an entirely
> different mechanism internally?

Hm...  I haven't thought much about that, but my immediate feeling is
that a syntactically clearer return functionality isn't as necessary in
lambda forms as in defuns.  Lambda forms are (in practise) quite short
and sweet, in my experience.

-- 
(domestic pets only, the antidote for overdose, milk.)
  larsi@gnus.org * Lars Magne Ingebrigtsen




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-26  9:24 ` return Miles Bader
  2010-11-26  9:36   ` return Lars Magne Ingebrigtsen
@ 2010-11-26  9:44   ` Tassilo Horn
  1 sibling, 0 replies; 31+ messages in thread
From: Tassilo Horn @ 2010-11-26  9:44 UTC (permalink / raw)
  To: emacs-devel; +Cc: Miles Bader

On Friday 26 November 2010 10:24:04 Miles Bader wrote:

Hi Miles,

> Doesn't every defun in CL establish a nil block?

No, it implicitly establishes a block named accordind to the "function
block name" of the function name, which in general is the function name.

(defun cl-fun ()
  (return "bing"))
==> *** - RETURN-FROM: no block named NIL is currently visible

(defun cl-fun ()
  (return-from cl-fun "bang"))
==> "bang"

Bye,
Tassilo



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-26  9:36   ` return Lars Magne Ingebrigtsen
@ 2010-11-26  9:54     ` Miles Bader
  2010-11-26 10:13       ` return Lars Magne Ingebrigtsen
  0 siblings, 1 reply; 31+ messages in thread
From: Miles Bader @ 2010-11-26  9:54 UTC (permalink / raw)
  To: emacs-devel

Lars Magne Ingebrigtsen <larsi@gnus.org> writes:
> No, it establishes a named block, so you can say
..
> Which is kinda annoying, in my opinion.  Especially if you have long
> function names and a lot of return-from forms.  Or you rename function
> names...

Hmm, kinda annoying or not, I think it would be silly to add a
slightly-incompatible form of return to elisp (regardless of whether it
uses a new name or not); it's just confusing, for a very small gain.

If we add this, I think we should just do so compatibly with CL, and
then change cl.el to use the new mechanism.

-Miles

-- 
Accordion, n. An instrument in harmony with the sentiments of an assassin.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-26  9:54     ` return Miles Bader
@ 2010-11-26 10:13       ` Lars Magne Ingebrigtsen
  0 siblings, 0 replies; 31+ messages in thread
From: Lars Magne Ingebrigtsen @ 2010-11-26 10:13 UTC (permalink / raw)
  To: emacs-devel

Miles Bader <miles@gnu.org> writes:

> Hmm, kinda annoying or not, I think it would be silly to add a
> slightly-incompatible form of return to elisp (regardless of whether it
> uses a new name or not); it's just confusing, for a very small gain.

I'd rather have return-from in Emacs Lisp than nothing, but I do think
the potential for confusion with, say, freturn, is pretty minimal.  It
does what you'd expect from almost all other languages -- return from
the current function.

I don't think this is a very important point, so I won't argue it
further (after this message), but if we have to possibility to add a new
language feature, I think we're slightly wasting an opportunity if we
don't make the new language feature as compelling as possible.

-- 
(domestic pets only, the antidote for overdose, milk.)
  larsi@gnus.org * Lars Magne Ingebrigtsen




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-26  8:57 return Lars Magne Ingebrigtsen
  2010-11-26  9:19 ` return Tassilo Horn
  2010-11-26  9:24 ` return Miles Bader
@ 2010-11-26 14:59 ` Stefan Monnier
  2010-11-26 15:45   ` return Lars Magne Ingebrigtsen
  2 siblings, 1 reply; 31+ messages in thread
From: Stefan Monnier @ 2010-11-26 14:59 UTC (permalink / raw)
  To: emacs-devel

> While debugging and fixing stuff, I often find myself in the situation
> of adding more if/cond statements to a function and pushing the original
> body further in.  I think this usually leads to less clear code.

If this is only "while debugging and fixing stuff", I don't think it's
a serious enough problem to warrant such a change.  Note that my
preference to stick with the current situation in his respect is not
just out of inertia but also because I like a more functional style
of programming: usually/often you can find a formulation that's just as
elegant without the need for an early exit.

> This isn't the only use case, of course.  A lot of functions in Emacs go
> through pretty awkward contortions to end loops when certain conditions
> occur.

I tend to agree that early exit from loops would be sometimes welcome.
Currently, people either use catch/throw, or contortions, or just decide
not to exit early.  Also our while loops have no way to return a value,
so a `return' from a while loop can make the code *more* functional.
In functional programming language early exit constructs are rarely
needed because loops are written using recursion, so it's easy to exit
early: just don't recurse.  One constructs which would improve the
situation (without adding early exits) would be to add an `until': it's
just like `while' except that the condition is reversed *and* that the
non-nil final condition is returned as value of the `until' expression.
Very often (until (progn foo bar) baz) works well.  But I'd be OK with
adding a `return' from while loops: after all `while' loops are
naturally imperative, so such a `return' would not make things
less imperative.


        Stefan



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-26 14:59 ` return Stefan Monnier
@ 2010-11-26 15:45   ` Lars Magne Ingebrigtsen
  2010-11-26 18:40     ` return Stefan Monnier
  0 siblings, 1 reply; 31+ messages in thread
From: Lars Magne Ingebrigtsen @ 2010-11-26 15:45 UTC (permalink / raw)
  To: emacs-devel

Stefan Monnier <monnier@IRO.UMontreal.CA> writes:

> If this is only "while debugging and fixing stuff", I don't think it's
> a serious enough problem to warrant such a change.

Well, not, that was just what I was doing moments before writing the
email, so it just reminded me.  But the style of doing

(defun foo ()
  (unless condition
    (return bar))
  .. compute a bit more
  (unless other-condition
    (return zot))
  ... do the real foo work here)

feels kinda natural.  It's what some people calls "guard based
programming".  Without it, you end up with

(defun foo ()
  (if (not condition)
      bar
    .., compute a bit more
    (if (not other-condition)
        zot
      ... do the real work)))

where you have to read quite a lot of the code to find out what the exit
points are.  I feel like the former style often makes it easier to read
the code.
  
> Note that my preference to stick with the current situation in his
> respect is not just out of inertia but also because I like a more
> functional style of programming: usually/often you can find a
> formulation that's just as elegant without the need for an early exit.

Yeah, you're right, early returns aren't very functional in style.

> I tend to agree that early exit from loops would be sometimes welcome.

[...]

> But I'd be OK with adding a `return' from while loops: after all
> `while' loops are naturally imperative, so such a `return' would not
> make things less imperative.

while, dolist, dotimes...

The temptation to rewrite loops using the cl-macs `loop' macro can
sometimes be overwhelming, since you can return from the loop easier.
But using `loop' doesn't seem to be in the Emacs Lisp style, I think --
it's not used a lot, which kinda leads me to think that most Emacs Lisp
programmers don't really know how it works.

And it's not always contortion-free, either.

-- 
(domestic pets only, the antidote for overdose, milk.)
  larsi@gnus.org * Lars Magne Ingebrigtsen




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-26 15:45   ` return Lars Magne Ingebrigtsen
@ 2010-11-26 18:40     ` Stefan Monnier
  2010-11-27  1:31       ` return Lars Magne Ingebrigtsen
  0 siblings, 1 reply; 31+ messages in thread
From: Stefan Monnier @ 2010-11-26 18:40 UTC (permalink / raw)
  To: emacs-devel

>> If this is only "while debugging and fixing stuff", I don't think it's
>> a serious enough problem to warrant such a change.
> Well, not, that was just what I was doing moments before writing the
> email, so it just reminded me.  But the style of doing

> (defun foo ()
>   (unless condition
>     (return bar))
>   .. compute a bit more
>   (unless other-condition
>     (return zot))
>   ... do the real foo work here)

> feels kinda natural.  It's what some people calls "guard based
> programming".  Without it, you end up with

> (defun foo ()
>   (if (not condition)
>       bar
>     .., compute a bit more
>     (if (not other-condition)
>         zot
>       ... do the real work)))

> where you have to read quite a lot of the code to find out what the exit
> points are.  I feel like the former style often makes it easier to read
> the code.

Of course, that's what `cond' is for.  Now, I know it doesn't always cut
it, but I'm still unconvinced that the rare cases where it would really
improve the code are worth all the cases where people will end up using
it instead of using a cleaner functional style.

>> But I'd be OK with adding a `return' from while loops: after all
>> `while' loops are naturally imperative, so such a `return' would not
>> make things less imperative.
> while, dolist, dotimes...

Yes, same thing.

> The temptation to rewrite loops using the cl-macs `loop' macro can
> sometimes be overwhelming, since you can return from the loop easier.

You can try the `do' macro, which is not as flexible as `loop', but does
correspond fairly simply to a functional recursive loop.  We could also
provide (and encourage the use of) Scheme's named-let, although it tends
to be "too powerful" (not inherently tail-recursive, so making it
efficient (CPU-wise and stack-space-wise) would require a good bit more
work).

> But using `loop' doesn't seem to be in the Emacs Lisp style, I think --
> it's not used a lot, which kinda leads me to think that most Emacs Lisp
> programmers don't really know how it works.

Indeed.


        Stefan



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-26 18:40     ` return Stefan Monnier
@ 2010-11-27  1:31       ` Lars Magne Ingebrigtsen
  2010-11-27  2:49         ` return Stefan Monnier
  0 siblings, 1 reply; 31+ messages in thread
From: Lars Magne Ingebrigtsen @ 2010-11-27  1:31 UTC (permalink / raw)
  To: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

>> (defun foo ()
>>   (unless condition
>>     (return bar))
>>   .. compute a bit more
>>   (unless other-condition
>>     (return zot))
>>   ... do the real foo work here)

[...]

> Of course, that's what `cond' is for.

Really?  You would write

(defun foo ()
  (cond
    ((not condition) bar)
    ((progn
      ..compute a bit more
      (not other-condition))
      zot)
    (t
     ... do the real foo work here)))

?

I think that's slightly, er, disgusting.  :-)

> You can try the `do' macro, which is not as flexible as `loop', but does
> correspond fairly simply to a functional recursive loop.

Nobody understands `do'.  The `do' syntax makes, well, everything else
look obvious.

-- 
(domestic pets only, the antidote for overdose, milk.)
  larsi@gnus.org * Lars Magne Ingebrigtsen




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-27  1:31       ` return Lars Magne Ingebrigtsen
@ 2010-11-27  2:49         ` Stefan Monnier
  2010-11-27  3:06           ` return Lars Magne Ingebrigtsen
  0 siblings, 1 reply; 31+ messages in thread
From: Stefan Monnier @ 2010-11-27  2:49 UTC (permalink / raw)
  To: emacs-devel

> I think that's slightly, er, disgusting.  :-)

Well, I often find the "..compute a bit more" part, er, disgusting in
the first place anyway.


        Stefan



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-27  2:49         ` return Stefan Monnier
@ 2010-11-27  3:06           ` Lars Magne Ingebrigtsen
  2010-12-03 18:41             ` return Chong Yidong
  0 siblings, 1 reply; 31+ messages in thread
From: Lars Magne Ingebrigtsen @ 2010-11-27  3:06 UTC (permalink / raw)
  To: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

>> I think that's slightly, er, disgusting.  :-)
>
> Well, I often find the "..compute a bit more" part, er, disgusting in
> the first place anyway.

OK, I'll try harder in the future not to suggest Emacs-level features
(that seeming every other languages and environments have) while Emacs
shuffles towards a single-digit number of contributors.

-- 
(domestic pets only, the antidote for overdose, milk.)
  larsi@gnus.org * Lars Magne Ingebrigtsen




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-27  3:06           ` return Lars Magne Ingebrigtsen
@ 2010-12-03 18:41             ` Chong Yidong
  2010-12-03 18:43               ` return Miles Bader
  0 siblings, 1 reply; 31+ messages in thread
From: Chong Yidong @ 2010-12-03 18:41 UTC (permalink / raw)
  To: emacs-devel

Lars Magne Ingebrigtsen <larsi@gnus.org> writes:

> OK, I'll try harder in the future not to suggest Emacs-level features
> (that seeming every other languages and environments have)

As Tassilo Horn explained, catch/throw does exactly what you wanted.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-03 18:41             ` return Chong Yidong
@ 2010-12-03 18:43               ` Miles Bader
  2010-12-03 19:46                 ` return Chong Yidong
  0 siblings, 1 reply; 31+ messages in thread
From: Miles Bader @ 2010-12-03 18:43 UTC (permalink / raw)
  To: Chong Yidong; +Cc: emacs-devel

Chong Yidong <cyd@stupidchicken.com> writes:
>> OK, I'll try harder in the future not to suggest Emacs-level features
>> (that seeming every other languages and environments have)
>
> As Tassilo Horn explained, catch/throw does exactly what you wanted.

I presume he wants something without the overhead tho...

-Miles

-- 
Run away!  Run away!



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-03 18:43               ` return Miles Bader
@ 2010-12-03 19:46                 ` Chong Yidong
  2010-12-03 21:26                   ` return Chong Yidong
  0 siblings, 1 reply; 31+ messages in thread
From: Chong Yidong @ 2010-12-03 19:46 UTC (permalink / raw)
  To: Miles Bader; +Cc: emacs-devel

Miles Bader <miles@gnu.org> writes:

> Chong Yidong <cyd@stupidchicken.com> writes:
>>> OK, I'll try harder in the future not to suggest Emacs-level features
>>> (that seeming every other languages and environments have)
>>
>> As Tassilo Horn explained, catch/throw does exactly what you wanted.
>
> I presume he wants something without the overhead tho...

You mean the performance overhead from adding an extra internal_catch?
I doubt there's a free lunch here; adding a "return" or "return-from"
mechanism would also add overhead, and that overhead would apply to
every single funcall.  Still, it's a worthwhile experiment to implement
"return"/"return-from" and see how big the performance impact is.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-03 19:46                 ` return Chong Yidong
@ 2010-12-03 21:26                   ` Chong Yidong
  2010-12-03 22:29                     ` return Stefan Monnier
  2010-12-03 22:44                     ` return Chong Yidong
  0 siblings, 2 replies; 31+ messages in thread
From: Chong Yidong @ 2010-12-03 21:26 UTC (permalink / raw)
  To: Miles Bader; +Cc: emacs-devel

Chong Yidong <cyd@stupidchicken.com> writes:

> You mean the performance overhead from adding an extra internal_catch?
> I doubt there's a free lunch here; adding a "return" or "return-from"
> mechanism would also add overhead, and that overhead would apply to
> every single funcall.  Still, it's a worthwhile experiment to implement
> "return"/"return-from" and see how big the performance impact is.

I did a quick experiment, and turns out built-in blocking is a little
faster than an explicit `catch', mostly because of reduced consing.  I
tested with a function that runs 500,000 tight `while' loops:

  (defun test-loop-with-catch ()
    (dotimes (ii 500000)
      (let ((ll '(1 2 3 4 5 6 7 8 9 10)))
        (catch 'exit
	  (while ll (setq ll (cdr ll)))))))

The run time is 1.164s, as opposed to 1.084s with the `catch' omitted.
So an explicit `catch' adds about 10 percent to the run time.

If I hack Fwhile to perform a catch internally, the runtime for the test
function (with the `catch' omitted) is 1.057s, within the margin of
error of the unhacked Emacs.

This (very limited) test indicates that adding built-in support for
block, return, and return-from should have little performance impact.
(Though the block tags ought to use a specialized obarray instead of
what cl-macs.el does, which is to intern them as "--cl-block-%s--".)

Does anyone see a deeper problem with providing such functions?



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-03 21:26                   ` return Chong Yidong
@ 2010-12-03 22:29                     ` Stefan Monnier
  2010-12-03 23:00                       ` return Chong Yidong
  2010-12-03 22:44                     ` return Chong Yidong
  1 sibling, 1 reply; 31+ messages in thread
From: Stefan Monnier @ 2010-12-03 22:29 UTC (permalink / raw)
  To: Chong Yidong; +Cc: emacs-devel, Miles Bader

>> You mean the performance overhead from adding an extra internal_catch?
>> I doubt there's a free lunch here; adding a "return" or "return-from"
>> mechanism would also add overhead, and that overhead would apply to
>> every single funcall.  Still, it's a worthwhile experiment to implement
>> "return"/"return-from" and see how big the performance impact is.

> I did a quick experiment, and turns out built-in blocking is a little
> faster than an explicit `catch', mostly because of reduced consing.  I
> tested with a function that runs 500,000 tight `while' loops:

>   (defun test-loop-with-catch ()
>     (dotimes (ii 500000)
>       (let ((ll '(1 2 3 4 5 6 7 8 9 10)))
>         (catch 'exit
> 	  (while ll (setq ll (cdr ll)))))))

> The run time is 1.164s, as opposed to 1.084s with the `catch' omitted.
> So an explicit `catch' adds about 10 percent to the run time.

> If I hack Fwhile to perform a catch internally, the runtime for the test
> function (with the `catch' omitted) is 1.057s, within the margin of
> error of the unhacked Emacs.

A few questions:
- how do you explain that Fwhile with internal catch is faster (1.057 <
  1.084) than without an internal catch?  Or is that what you mean by
  "within the margin of error"?
- You seem to be measuring time for the interpreted code, is that right?
  If so, I think it would be more interesting to measure the time for
  byte-code.

The little tests I've performed seem to indicate that for interpreted
code the extra `catch' doesn't make much of a difference, but for the
compiled version of your test, the difference is around 20%.

> This (very limited) test indicates that adding built-in support for
> block, return, and return-from should have little performance impact.
> (Though the block tags ought to use a specialized obarray instead of
> what cl-macs.el does, which is to intern them as "--cl-block-%s--".)

If we add support for C-like exit statements (break/return, that only
work locally and hence don't add any extra run-time cost) to the
byte-compiler, that would be nice.


        Stefan



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-03 21:26                   ` return Chong Yidong
  2010-12-03 22:29                     ` return Stefan Monnier
@ 2010-12-03 22:44                     ` Chong Yidong
  2010-12-04  9:22                       ` return Helmut Eller
  1 sibling, 1 reply; 31+ messages in thread
From: Chong Yidong @ 2010-12-03 22:44 UTC (permalink / raw)
  To: Miles Bader; +Cc: emacs-devel

Chong Yidong <cyd@stupidchicken.com> writes:

> This (very limited) test indicates that adding built-in support for
> block, return, and return-from should have little performance impact.
> (Though the block tags ought to use a specialized obarray instead of
> what cl-macs.el does, which is to intern them as "--cl-block-%s--".)
>
> Does anyone see a deeper problem with providing such functions?

Thinking about this some more, I do see a couple of snags.  First, the
`dotimes' and `dolist' macros use `while' internally, so if we introduce
a built-in `return' that exits from `while', it's hard to make that same
`return' exit correctly from `dotimes'/`dolist'.  One solution might be
to introduce something like `while-noblock' and use that in `dotimes'
and `dolist'.

Also, block tags should have lexical scope, so in order to implement
`block' properly we probably ought to wait for the lexical binding
changes.  I think it is currently possible to implement `return' for
exiting unnamed blocks, and leave `block'/`return-from' for the future,
but I dunno if doing it like that is worth our while.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-03 22:29                     ` return Stefan Monnier
@ 2010-12-03 23:00                       ` Chong Yidong
  2010-12-04  1:35                         ` return Stefan Monnier
  2010-12-06 16:13                         ` return Davis Herring
  0 siblings, 2 replies; 31+ messages in thread
From: Chong Yidong @ 2010-12-03 23:00 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel, Miles Bader

Stefan Monnier <monnier@IRO.UMontreal.CA> writes:

> A few questions:
> - how do you explain that Fwhile with internal catch is faster (1.057 <
>   1.084) than without an internal catch?  Or is that what you mean by
>   "within the margin of error"?
> - You seem to be measuring time for the interpreted code, is that right?
>   If so, I think it would be more interesting to measure the time for
>   byte-code.
>
> The little tests I've performed seem to indicate that for interpreted
> code the extra `catch' doesn't make much of a difference, but for the
> compiled version of your test, the difference is around 20%.

Hmm, yes, I was testing on interpreted code.  Here is a new test using a
byte-compiled file:

(defun test-loop-nocatch ()
  (dotimes (ii 2000000)
    (let ((ll '(1 2 3 4 5 6 7 8 9 10)))
      (while ll
	(setq ll (cdr ll))))))

Averaging over ten runs, this takes 1.351 +- 0.022s without an internal
catch, and 1.371 +- 0.034s with an internal catch.

Using the Emacs without an internal-catch, I tested on a byte-compiled
version of a function with a catch inserted manually into the loop:

(defun test-loop-catch ()
  (dotimes (ii 2000000)
    (let ((ll '(1 2 3 4 5 6 7 8 9 10)))
      (while ll
        (catch 'exit
          (setq ll (cdr ll)))))))

The result is 1.725 +- 0.033s.

The simplistic "internal catch" implementation I used is this:

*** src/eval.c	2010-10-26 22:23:09 +0000
--- src/eval.c	2010-12-03 22:59:10 +0000
***************
*** 1054,1065 ****
    return unbind_to (count, elt);
  }
  
! DEFUN ("while", Fwhile, Swhile, 1, UNEVALLED, 0,
!        doc: /* If TEST yields non-nil, eval BODY... and repeat.
! The order of execution is thus TEST, BODY, TEST, BODY and so on
! until TEST returns nil.
! usage: (while TEST BODY...)  */)
!   (Lisp_Object args)
  {
    Lisp_Object test, body;
    struct gcpro gcpro1, gcpro2;
--- 1054,1061 ----
    return unbind_to (count, elt);
  }
  
! Lisp_Object
! internal_while (Lisp_Object args)
  {
    Lisp_Object test, body;
    struct gcpro gcpro1, gcpro2;
***************
*** 1078,1083 ****
--- 1074,1090 ----
    return Qnil;
  }
  
+ DEFUN ("while", Fwhile, Swhile, 1, UNEVALLED, 0,
+        doc: /* If TEST yields non-nil, eval BODY... and repeat.
+ The order of execution is thus TEST, BODY, TEST, BODY and so on
+ until TEST returns nil.
+ usage: (while TEST BODY...)  */)
+   (Lisp_Object args)
+ {
+   int count = SPECPDL_INDEX ();
+   return internal_catch (Qexit, &internal_while, args);
+ }
+ 
  DEFUN ("macroexpand", Fmacroexpand, Smacroexpand, 1, 2, 0,
         doc: /* Return result of expanding macros at top level of FORM.
  If FORM is not a macro call, it is returned unchanged.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-03 23:00                       ` return Chong Yidong
@ 2010-12-04  1:35                         ` Stefan Monnier
  2010-12-04  3:23                           ` return Chong Yidong
  2010-12-06 16:13                         ` return Davis Herring
  1 sibling, 1 reply; 31+ messages in thread
From: Stefan Monnier @ 2010-12-04  1:35 UTC (permalink / raw)
  To: Chong Yidong; +Cc: emacs-devel, Miles Bader

> Hmm, yes, I was testing on interpreted code.  Here is a new test using a
> byte-compiled file:

> (defun test-loop-nocatch ()
>   (dotimes (ii 2000000)
>     (let ((ll '(1 2 3 4 5 6 7 8 9 10)))
>       (while ll
> 	(setq ll (cdr ll))))))

> Averaging over ten runs, this takes 1.351 +- 0.022s without an internal
> catch, and 1.371 +- 0.034s with an internal catch.

The Fwhile function is not used in the byte-compiled version of the
`while' special form.  Instead, the byte-compiler turns it into a bunch
of byte-codes like test and jump to label.
So your "with an internal catch" version should perform 100% identically
in this test ;-)


        Stefan



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-11-26  9:19 ` return Tassilo Horn
@ 2010-12-04  2:36   ` Fren Zeee
  2010-12-04  3:04     ` Common vs Emacs Lisp (was: Re: return) Chad Brown
  2010-12-04  6:18     ` return Stephen J. Turnbull
  0 siblings, 2 replies; 31+ messages in thread
From: Fren Zeee @ 2010-12-04  2:36 UTC (permalink / raw)
  To: Tassilo Horn; +Cc: Lars Magne Ingebrigtsen, emacs-devel

On Fri, Nov 26, 2010 at 1:19 AM, Tassilo Horn <tassilo@member.fsf.org> wrote:
> On Friday 26 November 2010 09:57:21 Lars Magne Ingebrigtsen wrote:
>> Wouldn't it be nice if Emacs Lisp had a workable early-return
>> mechanism?
>
> I guess so.  One option you didn't list is using catch/throw:
>
> (defun foo (n)
>  (catch 'val
>    (dostuff)
>    (when zot
>      (throw 'val t))))
>
> That's at least not something you need the cl package for.
>

Why not just throw the whole elisp and use CL to run emacs with
lexical scoping ?

What particular advantages do there accrue from the dyn scoped elisp
and by incrementally making fixes to this dinosaur ?

Franz Xe



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: Common vs Emacs Lisp (was: Re: return)
  2010-12-04  2:36   ` return Fren Zeee
@ 2010-12-04  3:04     ` Chad Brown
  2010-12-04 19:48       ` Fren Zeee
  2010-12-04  6:18     ` return Stephen J. Turnbull
  1 sibling, 1 reply; 31+ messages in thread
From: Chad Brown @ 2010-12-04  3:04 UTC (permalink / raw)
  To: Fren Zeee; +Cc: Emacs Developers

On Dec 3, 2010, at 6:36 PM, Fren Zeee wrote:
> 
> Why not just throw the whole elisp and use CL to run emacs with
> lexical scoping ?
> 
> What particular advantages do there accrue from the dyn scoped elisp
> and by incrementally making fixes to this dinosaur ?

Conservatively, I'll say at least 10,000 programmer-hours of existing, heavily
used (by emacs standards) elisp libraries, systems, programs, etc.

There are/have been projects that recreate emacs in Scheme, Common 
Lisp, and Tcl (that I know of).  None of them have done very well when
forced head-to-head with Emacs (Tcl doing the best, due to environment).

I suspect that I'd get widespread agreement from emacs developers to
a statement like the following:

	Common Lisp contains some good stuff that I'd like to see in Emacs 
	and a large amount of stuff that I'd never want to see in Emacs, even
	if it meant giving up the former.

..and that assumes that someone else magically does the work.

For CL in particular, it looks like Climacs could use some help.

*Chad


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-04  1:35                         ` return Stefan Monnier
@ 2010-12-04  3:23                           ` Chong Yidong
  0 siblings, 0 replies; 31+ messages in thread
From: Chong Yidong @ 2010-12-04  3:23 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel, Miles Bader

Stefan Monnier <monnier@IRO.UMontreal.CA> writes:

> The Fwhile function is not used in the byte-compiled version of the
> `while' special form.  Instead, the byte-compiler turns it into a bunch
> of byte-codes like test and jump to label.
> So your "with an internal catch" version should perform 100% identically
> in this test ;-)

Ah, I see.  I guess the key problem is the unacceptably large
performance difference, in byte compiled code, when we wrap `while' in
an additional `catch' (1.75s vs 1.4s without the `catch' in my test).
The `catch' byte operation does this:

    case Bcatch:
      {
        Lisp_Object v1;
        BEFORE_POTENTIAL_GC ();
        v1 = POP;
        TOP = internal_catch (TOP, Feval, v1);
        AFTER_POTENTIAL_GC ();
        break;
      }

So presumably it's the Feval that's killing us.  Hmm.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-04  2:36   ` return Fren Zeee
  2010-12-04  3:04     ` Common vs Emacs Lisp (was: Re: return) Chad Brown
@ 2010-12-04  6:18     ` Stephen J. Turnbull
  2010-12-04  6:49       ` return Leo
  1 sibling, 1 reply; 31+ messages in thread
From: Stephen J. Turnbull @ 2010-12-04  6:18 UTC (permalink / raw)
  To: Fren Zeee; +Cc: Tassilo Horn, Lars Magne Ingebrigtsen, emacs-devel

Fren Zeee writes:

 > Why not just throw the whole elisp and use CL to run emacs with
 > lexical scoping ?

A better question might be "why not build elisp on top of a more
efficient extension language?"  Mike Sperber <sperber@xemacs.org> did
some experiments with Scheme48 several years ago that he claims
indicate that a Scheme48 implementation of elisp is quite competitive
with XEmacs Lisp (mostly written in C).  Experiments done in about
2000 indicated that the XEmacs virtual machine was about 15% faster
than Emacs's, and 10% faster than Python's.  I suppose we *now* lag
behind the state of the art somewhat, but for a Scheme implementation
of Emacs Lisp to be competitive with XEmacs's C implementation at that
time suggests that a well-performing implementation of elisp in
another language suitable for use as an extension language for Emacs
is quite feasible (a "SMOP", as they say).

Note that Python (at least) has adopted this approach, with a core
language definition that is 99% or so independent of implementation
(cf. CPython, Cython nee pyrex, Stackless Python, Jython, IronPython,
and PyPy).

I doubt this will appeal to core Emacs hackers (I hear loud echoes of
YAGNI from the future, especially since Miles's "lexbind" branch is
scheduled for addition to Emacs 24 AIUI), but you might want to look
into it, at least try a couple of implementations of Python on the
same script a few times to see how little it could matter to users,
and then talk to Mike about somehow reviving his project or at least
getting advice (eg, if you prefer a different substrate language --
but I suspect Common Lisp would a lot more work to get decent
performance than a Scheme).

Speaking of Common Lisp, since it has a package scheme, you might be
able to implement the elisp interpreter by using the elisp
interpreter, and put in in a package that uses elisp eval instead of
Common Lisp's eval.  (Maybe I'm just smoking too much pakalolo. :-)

 > What particular advantages do there accrue from the dyn scoped elisp
 > and by incrementally making fixes to this dinosaur ?

Read Richard's screed about Emacs and its dependence on dynamic
scoping for why dynamic scoping is a feature in a language designed
for implementing editors.  (It's an MIT AI Lab working paper but it's
publicly available online somewhere.)  Of course like Common Lisp you
could default to lexical scoping and require declaration of
dynamically-scoped variables; Richard has said that on this list so
there's no disagreement on that point.  However, some form of dynamic
scoping is needed, and needed frequently even in modern Emacs,
according to him.  OTOH, AIUI he doesn't think dynamic scoping causes
problems all that often, lexical scoping is a bugaboo of minds that
have taken too many language design courses. :-)

The pragmatic advantage to reusing the code base based on the now-
archaic Emacs Lisp is that Emacs hackers rarely ;-) write code
designed to isolate use of dynamic scoping to where it's needed, so it
would be a huge effort to (1) find all those dynamically-scoped
variables, (2) identify the ones that can be lexical, (3) change them,
and (4) debug the product.

Like knowledge based on "obsolete" scientific paradigms, code
developed using "obsolete" programming paradigms is too valuable in
use to throw away.  It gets gradually replaced over time, and only
rarely "just because we can't bear to look at that any more!"  The
Emacs-Lisp-in-a-better-extension-language idea is attractive because
it makes that evolution possible.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-04  6:18     ` return Stephen J. Turnbull
@ 2010-12-04  6:49       ` Leo
  0 siblings, 0 replies; 31+ messages in thread
From: Leo @ 2010-12-04  6:49 UTC (permalink / raw)
  To: Stephen J. Turnbull
  Cc: Lars Magne Ingebrigtsen, Tassilo Horn, Fren Zeee, emacs-devel

On 2010-12-04 06:18 +0000, Stephen J. Turnbull wrote:
> A better question might be "why not build elisp on top of a more
> efficient extension language?"

That would be many people's dream comes true. Looks like guile has
already implemented elisp. I wonder if the work to move emacs over guile
will take off after guile 2.0.

Cheers,
Leo



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-03 22:44                     ` return Chong Yidong
@ 2010-12-04  9:22                       ` Helmut Eller
  0 siblings, 0 replies; 31+ messages in thread
From: Helmut Eller @ 2010-12-04  9:22 UTC (permalink / raw)
  To: emacs-devel

* Chong Yidong [2010-12-03 22:44] writes:

> Also, block tags should have lexical scope, so in order to implement
> `block' properly we probably ought to wait for the lexical binding
> changes.  I think it is currently possible to implement `return' for
> exiting unnamed blocks, and leave `block'/`return-from' for the future,
> but I dunno if doing it like that is worth our while.

What's an "unnamed block"?  Is that the same as a block with name nil?

The compiler should recognize when a block tag doesn't escape and use
simple byte-goto instructions for that case.  Would that be difficult?

Helmut




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: Common vs Emacs Lisp (was: Re: return)
  2010-12-04  3:04     ` Common vs Emacs Lisp (was: Re: return) Chad Brown
@ 2010-12-04 19:48       ` Fren Zeee
  2010-12-05 10:20         ` Chad Brown
  0 siblings, 1 reply; 31+ messages in thread
From: Fren Zeee @ 2010-12-04 19:48 UTC (permalink / raw)
  To: Chad Brown; +Cc: Emacs Developers

Chad,

As soon as I finished composing my earlier reply, the idea of backward
compatibility and legacy code immediately struck me and the bane of a
company like microsoft from dos to win3.x to win95 to NT4 .... which
had an "unlimited" supply of money and workforce.

I agree with all of what you say. Only that I want you to fill in some
sharp example details in the pithy and poetic statement you have made.

It would help us in our times of frustration of the pros and cons. :)))

Cheers,
Franz Xe

On Fri, Dec 3, 2010 at 7:04 PM, Chad Brown <yandros@mit.edu> wrote:
> On Dec 3, 2010, at 6:36 PM, Fren Zeee wrote:
>>
>> Why not just throw the whole elisp and use CL to run emacs with
>> lexical scoping ?
>>
>> What particular advantages do there accrue from the dyn scoped elisp
>> and by incrementally making fixes to this dinosaur ?
>
> Conservatively, I'll say at least 10,000 programmer-hours of existing, heavily
> used (by emacs standards) elisp libraries, systems, programs, etc.
>
> There are/have been projects that recreate emacs in Scheme, Common
> Lisp, and Tcl (that I know of).  None of them have done very well when
> forced head-to-head with Emacs (Tcl doing the best, due to environment).
>
> I suspect that I'd get widespread agreement from emacs developers to
> a statement like the following:
>
>        Common Lisp contains some good stuff that I'd like to see in Emacs
>        and a large amount of stuff that I'd never want to see in Emacs, even
>        if it meant giving up the former.
>
> ..and that assumes that someone else magically does the work.
>
> For CL in particular, it looks like Climacs could use some help.
>
> *Chad



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: Common vs Emacs Lisp (was: Re: return)
  2010-12-04 19:48       ` Fren Zeee
@ 2010-12-05 10:20         ` Chad Brown
  0 siblings, 0 replies; 31+ messages in thread
From: Chad Brown @ 2010-12-05 10:20 UTC (permalink / raw)
  To: Fren Zeee; +Cc: Emacs Developers


On Dec 4, 2010, at 11:48 AM, Fren Zeee wrote:
> I agree with all of what you say. Only that I want you to fill in some
> sharp example details in the pithy and poetic statement you have made.

I'd guess that Gnus is the canonical example for most people.  My personal
hard-to-lose's would be Org and CC-Mode.  I gather that I might have been
indebted to CEDET/JDEE if I hadn't escaped Java when I did (which I count
a lucky break).

*Chad 



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-03 23:00                       ` return Chong Yidong
  2010-12-04  1:35                         ` return Stefan Monnier
@ 2010-12-06 16:13                         ` Davis Herring
  2010-12-06 17:15                           ` return Chong Yidong
  1 sibling, 1 reply; 31+ messages in thread
From: Davis Herring @ 2010-12-06 16:13 UTC (permalink / raw)
  To: Chong Yidong; +Cc: Miles Bader, Stefan Monnier, emacs-devel

> + {
> +   int count = SPECPDL_INDEX ();
> +   return internal_catch (Qexit, &internal_while, args);
> + }

Just for education, what is the purpose of `count' here in the absence of
an `unbind_to'?

Davis

-- 
This product is sold by volume, not by mass.  If it appears too dense or
too sparse, it is because mass-energy conversion has occurred during
shipping.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: return
  2010-12-06 16:13                         ` return Davis Herring
@ 2010-12-06 17:15                           ` Chong Yidong
  0 siblings, 0 replies; 31+ messages in thread
From: Chong Yidong @ 2010-12-06 17:15 UTC (permalink / raw)
  To: herring; +Cc: Miles Bader, Stefan Monnier, emacs-devel

"Davis Herring" <herring@lanl.gov> writes:

>> + {
>> +   int count = SPECPDL_INDEX ();
>> +   return internal_catch (Qexit, &internal_while, args);
>> + }
>
> Just for education, what is the purpose of `count' here in the absence of
> an `unbind_to'?

It was a typo.



^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2010-12-06 17:15 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-26  8:57 return Lars Magne Ingebrigtsen
2010-11-26  9:19 ` return Tassilo Horn
2010-12-04  2:36   ` return Fren Zeee
2010-12-04  3:04     ` Common vs Emacs Lisp (was: Re: return) Chad Brown
2010-12-04 19:48       ` Fren Zeee
2010-12-05 10:20         ` Chad Brown
2010-12-04  6:18     ` return Stephen J. Turnbull
2010-12-04  6:49       ` return Leo
2010-11-26  9:24 ` return Miles Bader
2010-11-26  9:36   ` return Lars Magne Ingebrigtsen
2010-11-26  9:54     ` return Miles Bader
2010-11-26 10:13       ` return Lars Magne Ingebrigtsen
2010-11-26  9:44   ` return Tassilo Horn
2010-11-26 14:59 ` return Stefan Monnier
2010-11-26 15:45   ` return Lars Magne Ingebrigtsen
2010-11-26 18:40     ` return Stefan Monnier
2010-11-27  1:31       ` return Lars Magne Ingebrigtsen
2010-11-27  2:49         ` return Stefan Monnier
2010-11-27  3:06           ` return Lars Magne Ingebrigtsen
2010-12-03 18:41             ` return Chong Yidong
2010-12-03 18:43               ` return Miles Bader
2010-12-03 19:46                 ` return Chong Yidong
2010-12-03 21:26                   ` return Chong Yidong
2010-12-03 22:29                     ` return Stefan Monnier
2010-12-03 23:00                       ` return Chong Yidong
2010-12-04  1:35                         ` return Stefan Monnier
2010-12-04  3:23                           ` return Chong Yidong
2010-12-06 16:13                         ` return Davis Herring
2010-12-06 17:15                           ` return Chong Yidong
2010-12-03 22:44                     ` return Chong Yidong
2010-12-04  9:22                       ` return Helmut Eller

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).