Showing posts with label lisp. Show all posts
Showing posts with label lisp. Show all posts

Wednesday, September 30, 2009

Emacs Tip #33: paredit

I recently found paredit. It is a mode which allows you to edit s-expressions (sexps) in a structured way.

Essentially it forces you to maintain balanced parentheses by always inserting/deleting matched pairs. It is very easy to modify the current sexp to: enclose (wrap) the next element, to merge it up one level (aka splice, i.e. remove a set of parentheses), to split the current sexp, etc.

It works with round parens (), square brackets [], curly braces {}, and angle braces <>. It also handles string quotes " the same way.

Deleting and killing of text also works nicely. For example, C-k is bound to 'paredit-kill which has the following behavior (note: | represents the cursor position):

(foo bar)|     ; Useless comment!
->
(foo bar)|

(|foo bar) ; Useful comment!
->
(|) ; Useful comment!

|(foo bar) ; Useless line!
->
|

(foo "|bar baz"
quux)
->
(foo "|"
quux)
It's taking a little getting used to, but I'm finding myself thinking less about where the current block of code ends than I used to. The parentheses just take care of themselves...

This is how I enabled it:


(defun turn-on-paredit-mode ()
(paredit-mode +1))

;; I didn't like C-j being overridden, I want it to still work the old way in lisp-interaction
(eval-after-load "paredit"
'(progn
(define-key paredit-mode-map (read-kbd-macro "C-j") nil)))

(add-hook 'emacs-lisp-mode-hook 'turn-on-paredit-mode)

The cheat sheet is probably the best way to get an idea of what is possible. Check it out here. Also, the command documentation is very complete (see the above which was grabbed right from the C-k documentation, so it's easy to remind yourself what is possible.

About the only problem I've had is when I've yanked code that has unbalanced parens. So I wrote a little advice which at least warns you and allows you to abort if the yanked text is unbalanced. It's not perfect, but it is a reasonable first implementation:

(defun paredit-check-region-for-yank ()
"run after yank and yank-pop to verify balanced parens"
(when paredit-mode
(save-excursion
(save-restriction
(narrow-to-region (point) (mark))
(condition-case nil
(check-parens)
(error
(if (not (y-or-n-p "The text inserted has unbalanced parentheses, continue? "))
(delete-region (point-min) (point-max)))))))))

(defadvice yank (after yank-check-parens activate)
(paredit-check-region-for-yank))
(defadvice yank-pop (after yank-pop-check-parens activate)
(paredit-check-region-for-yank))

Friday, June 12, 2009

Emacs Tip #30: igrep

Finding text in files is an everyday (every hour) occurrence for most programmers. And in Unix-land, people generally use grep to do the searching.

Of course Emacs has an interface to grep, which now (as of Emacs 22) even has interfaces for using both find and grep together to search through directory structures (grep-find, rgrep).

But, before those existed, someone wrote a nice package igrep.el, which has a very intuitive (IMO) interface.

M-x igrep-find RET RET /search/in/here/*.cxx RET

which will search for regexp in all the files with the extension .cxx underneath the /search/in/here/ directory.

As you'd expect, the results of the search show up in a buffer, and clicking on any one of them jumps you to that spot in the file. And the standard M-x next-error (aka C-x `) will jump you to the next match.

About the only thing I don't like is that the paths returned in the results are the fully expanded path. In other words, when the paths to the files are really long, most of the result buffer is taken up with just the pathname. So I wrote this little snippet of code which post-processes the buffer to trim the names relative to the search path entered (in the example above, all the /search/in/here/ would be removed from the file names.


(defun my-shorten-filenames-in-compilation (buffer &optional stat)
"remove the absolute filenames if they match the default-directory"
(interactive "b")
(save-excursion
(set-buffer buffer)
(goto-char (point-min))
(let ((buffer-read-only nil)
(base-dir (if (re-search-forward "find \\([^ ]+\\) " nil t)
(if (string-match "/$" (match-string 1))
(match-string 1)
(concat (match-string 1) "/"))
default-directory)))
(setq default-directory base-dir)
(while (re-search-forward (concat "^" default-directory) nil t)
(replace-match "")))))

(add-hook 'compilation-finish-functions 'my-shorten-filenames-in-compilation)


I do realize that the built-in M-x rgrep does this automatically, I just prefer the igrep interface.

Wednesday, September 3, 2008

The Web Interface Is Morphing Into Emacs

A while back, Steve Yegge wrote a post on Emacs and its future. My executive summary of his post is that Emacs needs to compete or die - and the competition is the browser.

This got a little thread started on the Emacs development list, but not much activity. If Steve's line of reasoning is correct, then I surmise Emacs is dead b/c the Emacs development team is not at all interested in competing with the browser. Heck, they recently had a discussion on moving from CVS to a modern (distributed) version control system - and while there appears to be a tentative decision to move to Bazaar for version control, main development is still on CVS.

I personally think Emacs is a niche product. Like Unix, it has a dedicated fan base and will be around for the foreseeable future. But, it is never going to grow a large base of end-users. It has got a lot of ... baggagehistory that turns some people off: GPL, RMS, lisp, (lack of) speed, text-based, carpal tunnel, etc.

I love Emacs and do most everything inside Emacs, but it is not and probably will never be hip, exciting, new, or perceived as leading edge technology.

What was my point? Oh, right, Ubiquity was recently announced at Mozilla Labs.

I watched the 6 minute video demo. And my first impression? It is Emacs, only instead of M-x you use C-SPC. Soon, people will tire of typing things out and come up with shortcuts based on various key combinations, and it'll turn into Emacs.

Of course, if Ubiquity does that, it'll be cool/hip, and people won't complain about the key-bindings.

It's just like XML over Lisp. The reaction to Lisp is, "OMG! Parentheses! Run!" On the other hand, XML (with twice the number of parenthesis</>) is cool/hip and folks have flocked to it.

Steve was right about the competition, the browser has fired the first salvo, and it looks a lot like Emacs.

Mozilla Labs » Introducing Ubiquity

Wednesday, July 30, 2008

Forcing Abstraction

I just read the post: Rail Spikes: Functional programming and looping by Jon. It's a nice post, and I completely agree - you should never write for/while/do-while loops.

My gripe here is that this post has to exist at all. Everyone should know this, it is fundamental to have collections and operate on the collection - not using indices into collections1. IMO, this should be taught from day 1 in programming courses. Kudos to Ruby for getting the syntax clean - the left to right parsing is easier on (English reading) programmers than Lisp's inside-out parsing.

It seems to me that "abstraction" is generally applied to only classes people write, and not much thought is given to the lower-level usages. In this case: looping constructs.

I find it interesting that many people won't move away from for/while/do-while loops. In my previous job, people actively resisted such efforts. I remember introducing BOOST_FOREACH into our C++ development (a poor replacement for map, but a step in the right direction), and I had to pull teeth to get folks to use it. And, as awkward as the STL algorithms may be to use, they're still easier than rolling your own loop to remove elements.

I think Jon has taken a (permanent) step up the abstraction layer, he's moved away from thinking about the mechanics of iteration to focusing on what he wants to get done. Isn't that what we're taught in our first class on abstraction?

The main reasons people seem to give for not taking the step Jon has are:

  1. language doesn't support it (Java/C++/C/whatever)
  2. need for speed
  3. for loops are easy

Language. The first is legitimate, and I don't see an easy way around this. I find it sad when languages do not allow this, but my whining doesn't make the problem go away. Let's move on.

Speed. Funny how most everyone thinks their code just needs to be fast and they just know the code they're writing is fast. We should all trust Knuth when he said "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil." If you really think you're smarter than Knuth, there's nothing I can say that will ever help you.

Easy. The third point, in my opinion, boils down to short-sightedness. People can be too narrow-minded (lazy) to want to learn something new. They might not see the fact they could remove an entire class of bugs from their code, forever. Folks don't realize the visual/mental clutter for loops add to their code. And, most importantly, for loops focus on the wrong thing (the indices, as opposed to the transformation) - see abstraction.

On a related abstraction tangent, a colleague of mine at Intel started writing a geometric template library for C++. One of the guiding ideas (not novel, but perhaps pushed to the extreme) was to remove 'if' statements from the code, as well as to remove all references to X/Y/Z. As an example, you might want to know whether the points a,b,c form a concave right angle2. One way is to have four different checks looking like:
if(
a.x == b.x &&
a.y < b.y &&
b.y == c.y &&
b.x < c.x
) || ...

repeated 3 more times with slight variation. Or, you could write a one-liner like so:

bool concave = (a.towards(b).left() == b.towards(c));

Teams of people were shown this example, contrasting 20 lines of code against the one-liner, and the response was almost universally, "so? what's the problem?"

I am truly stunned and left speechless when people cannot comprehend the difference in readability/maintainability of 20 lines of (mind-numbingly-repetitive-and-error-prone) code versus one line. We can debate whether the single line is simply readable or more elegant, but surely the 20 lines can be agreed as horrific.

After finally bridging that gap, people immediately ask why the GTL library does not provide direct access to the X/Y/Z coordinates. The question is usually phrased as, "Ok, ok, the one-liner is better. But what if I want to use the X/Y version?" Nobody has given an example where the isotropic (aka coordinate-free) code fails to provide something the X/Y interface provides. Yet people still cling to wanting that interface3.

I'm of the opinion we should force the level of abstraction up.

New languages should not have for/while/do-while loops, and (in this example), a geometric library should not provide an interface to X/Y/Z coordinates.

Personally, I'd rather not work with you if you can't wrap your mind around a higher level of abstraction4.

Footnotes:

  1. Sure, there are occasions in high-performance code where you might need to deal directly with indices, or write a for loop, but 97% of the time you don't, so give it up.

  2. This example assumes manhattan geometry, a common assumption in the EDA world.

  3. Yes, when you draw a rectangle, it might be clearer to access the X/Y coordinates directly. However, you'd better write that code in only one location. And why isn't your graphics library providing this for you in the first place?

  4. Interestingly, the GTL library provides significant speed improvement over other libraries because its isotropic bent allowed it to get rid of if statements which kill performance. I also found this post on high level optimization interesting, as well as Yegge's talk on dynamic languages (specifically on optimizations they can make).