A million lines of Lisp
psung.blogspot.com
psung.blogspot.com
The abstractions in emacs (modes and hooks are the ones discussed in the linked post) really aren't that strange, or unique, or even lisp-specific. Nor are they without analogs in other tools written in conventional imperative languages. An emacs written in python, frankly, wouldn't look that different. A mode by any other name would hook as smooth.
An emacs written in python, frankly, wouldn't look that different.
You're right, but I just can't imagine that. When I think of editing text, I think of writing Lisp to do it for me. I just can't comprehend doing it any other way... and my primary language is Perl :)
Such as http://peppy.flipturn.org/
But note that your claim wasn't really true before Python 2.5 (less than 2 years old). Imagine trying to write one of Emacs Lisp's most common special forms, save-excursion, in Python without context managers.
Even (especially!) Python programmers would admit that Python 2.5 is more powerful than Python 2.4. Emacs has a million lines of Lisp written with this feature not easily emulated in Python 2.4. The Lisp in my Emacs here uses save-excursion 9000 times; is there any Python program in the world that uses context managers 9000 times yet?
I'm impressed that a 20+-year-old language can hold its own with a 21st-century one under active development by somebody willing to (somewhat) break backwards compatibility. That's not to say that Elisp was this great 20 years ago -- it had, what, one kind of loop? -- but macros let it grow in directions that its users wanted. You can't really add editing-specific features with custom syntax to Python.
(defun save-excursion (function)
(let ((point (point))
(mark (mark))
...)
(funcall 'function)
(set-mark mark)
(goto-char point)))
Then you would say: (defun do-something nil (...))
(save-excursion 'do-something)
Ideal, no. Possible, yes. import os, subprocess
def macro(string, filename="~/macro.py"):
program_name = os.path.expanduser(filename)
out = open(program_name, 'w')
out.write(string)
out.close()
command = ["python"]
command.append(program_name)
subprocess.Popen(command)
macro("print 'Hello macro!'")
It seems like with a bit of ingenuity you could achieve any level of abstraction using a technique like that. def switch(case, actions):
macro(actions[case])
switch(2, ["print 'foo'", "print 'bar'", "print 'baz'"])>>> def boo(x):
... print "BOO"
... return x + 3
>>> [1, 2, 3, boo(4) + 0, 5][1]
will result in
BOO
2
. Each element of the list would be evaluated down to a result value, then the appropriate one is chosen. I'm pretty sure you would need to wrap every item in the switch list in a function just to delay the evaluation. This is just as true for if a different Python interpreter were executing generated code.
Adding control structures means extending the core language, such as being able to add a new exception-catching construct that could return evaluation to where it was thrown if, after passing through the exception handler, the preceding test no longer holds true. Or a command that backs up the surrounding for loop by one step. For example. What you wrote above would be useful for some things (generating boilerplate code, certainly), but it doesn't alter the underlying language.
>>> eval("print 'Hello macro!'")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
print 'Hello macro!'
^
SyntaxError: invalid syntax
I guess you can't do prints in an eval?- Lisp macros are expanded implicitly at compile-time so you don't have a big mess of temporary files;
- Lisp macro calls look just like the built-in operators, unlike your proposed "python macros" where all your macro invocations are a string
- For all but the most trivial cases your string-based scheme will force you to use concatenation and it will really get ugly really fast. In contrast, with Lisp it's much easier to represent code as data. For example, (+ 1 2) is the code to add 1 and 2 while '(+ 1 2) is a list representing the code to add 1 and 2 and you can then interpolate that easily in the generated code
- And your example is really awesomely constrained, it works but it also doesn't really help with anything. With Lisp, the macro facility scales from trivial problems where the macro code fits in 4 lines to very complex ones where the macro code spans 4 files.
For an example see http://wiki.tcl.tk/917
You couldn't use this to, say, allow Python's lambda to take multiple lines, or generate boilerplate code at compile time.
function evaluate(arg) {
return (arg instanceof Function) ? arg() : arg;
}
function sweach(tests, cases){
var ret = [];
for each (t in tests){
ret.push(evaluate(cases[t]));
}
return ret;
}
var twos = 0;
sweach([0, 1, 2, 1, 2, 1, 2], {2:function(){twos++;}});
// twos==3
It might get a bit messy though.