Thoughts on Perl and Emacs, technology and writing

The Problem with Scripting Language REPLs

I figured out what the problem is with Devel::REPL and the command line REPLs provided by Python and Ruby – evaluation is not a separate step. When I press enter, I’m forced to evaluate the current line.

In reality, usable REPLs, such as Emacs1, let you control when the read evaluate print sequence happens. I can craft the most beautiful function I can think of. Better still, if I change my mind, I can easily modify the function and redefine it with a keystroke. Well, a key chord at least.

In contrast, with Devel::REPL, once I have pressed enter, changing my mind is painful. Integrating it with Emacs comint will probably alleviate a lot of that pain.

Or better yet, as Anonymous recommends, I should take a look at Sepia or PDE which already have emacs integration. Having said that, basic integration is, what, 20 lines of emacs-lisp?

12 Responses

Python’s Idle, which is the default graphical python REPL shell, gives you a possibility to go back in history with Alt+P and it actually brings back whole function or class if you previously entered one. You can edit it and press enter again.

Textual python shell however, always gives you only a single line at a time from the history so you would have to sort of assemble the function or class line by line.

I can’t speak to Perl, but it sounds like you’re experiencing one problem with Devel::REPL (“once I have pressed enter, changing my mind is painful.”) then a) misunderstanding the difference between evaluation and execution; and/or b) making an inappropriate generalization to other tools and languages.

In Python’s standard REPL, you can absolutely redefine any function you input. If you’re using Emacs’ standard inferior-python-mode, you can edit your definitions in a python-mode buffer and send them to your REPL with a keybinding. Changing a function’s definition is as easy as hitting C-M-x again.

Additionally, if you kill/yank your code into the REPL, the standard M-p and M-n bindings bring back the entire input, not one line at time.

At no point is anything executed unless you request it. That is, if you send:

def foo(): return “foo”

The result is nothing; the function evaluated and bound to your local namespace. But if you send:

foo()

Then you get back “foo” — the function is executed.

Again, I can’t say how Emacs’ Perl stuff handles this. But having worked with a few different modes that use inferior processes for REPLs (Python, SQL, and Clojure at least), this is very standard behavior for inferior modes in Emacs.

@Caleb – this might be because of your experience with the command line REPLs. A good REPL, such as Emacs provides for Emacs Lisp is fairly nice to use.

@HS – yes, as you noticed, I’m talking about the base command-line functionality of python, irb and various Perl alternatives here. Devel::REPL and the textual python shell have the same issue in that you have to evaluate a line at a time.

I’ll need to do a straw poll of the pythonistas I know to see what proportion of them use IDLE or an alternative improved REPL.

@Ian – the answer is (c) – you’re misunderstanding what I’m saying. To be fair, that is partially my fault, I didn’t explicitly state I was talking about the command line REPLs and not a REPL running as an inferior process.

But I am surprised it wasn’t obvious to you as an emacser though from “Integrating [Devel::REPL] with Emacs comint will probably alleviate a lot of that pain.” I guess you didn’t read the whole post before replying.

Your misunderstanding also comes from the fact you haven’t used Devel::REPL. It _is_ possible to redefine subroutines (that is why I said painful, and not impossible). But redefining them a line at a time is painful.

Demonstrating defining one line functions in a REPL is not convincing.

I have used python a long time ago. It was okay, but for various reasons I am glad I am now able to use perl instead.

Real nice of you to criticize the non-substantive portions of my response, then silently edit your post to remove your abject failure to distinguish execution and evaluation, upon which the majority of my point hinged.

Your argument that Emacs is a REPL is absurd and contradictory (though Emacs does have REPLs and interfaces to REPLs).
Editing elisp in a buffer and hitting C-M-x to eval it isn’t a REPL, because, uh, the definition of a REPL is that it reads, evals, prints, and loops.

What you have there is a plain old edit-compile-debug cycle, it’s just that the same software does all of it for you.

1. To put into effect; carry out
7. Computer Science To run (a program or an instruction).

So, I’m executing my eval instruction. Whatever. I didn’t answer this point previously because I thought it was irrelevant. I still do. Feel free to disagree. I changed the word execution to evaluation because it confused you. And only you it seems.

If I’ve violated some unwritten etiquette rule here by not writing all over the place “Updated to avoid confusing Ian”, well, I’m not sorry about that. You can feel free to whine down here in the comments section. Or quote the original on your blog and pick holes in it to your hearts content.

If we’re arguing over semantics, I didn’t “argue” that Emacs is a REPL. I stated (somewhat incorrectly, I agree – that’s why I put kinda) that it was one.
Where is the argument?

Trying to strengthen your point by saying “absurd and contradictory” makes you look ridiculous. I think you meant “wrong” or “incorrect”. What am I contradicting?

And you’re wrong about C-M-x. When I press C-M-x, it reads, it evals, it prints (to *messages*) and it returns to the main emacs loop.

@Jared: I’m actually using PyCrust, which comes with wxPython, as my Pyhon REPL shell. No need to use textual python shell when I have X Window system running. PyCrust gives me completion and easy access to API documentation — when I type “(” after a function name, it displays tooltip with the documentation string. Two things I miss in Emacs (or to be more precise, in python-mode + ECB (CEDET)).

Also I’ve just find out that, in Emacs’ python-shell, if you press C-j instead of a newline is entered but the edited line is not evaluated/executed — you can continue editing next line. It does not work in textual interface though.