Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Practical Emacs Lisp (ergoemacs.org)
103 points by brudgers on Sept 11, 2015 | hide | past | favorite | 56 comments


Inexperienced Emacs Lisp programmers coming to this site for the first time should be aware that Xah Lee's code is generally not very idiomatic. He discusses it himself here: http://ergoemacs.org/misc/emacs_lisp_coding_style_language_i...


I'm an experienced Lisp programmer and most of it looks fine to me from a random sampling, as far as formatting goes.

Sometimes a closing parenthesis is on a line by itself:

   (defun foo ()
      ...
   )

   (let ((x y)
         (z w)
        )
      ...)
Historically, it's not been unheard of that Lisp experts (even implementors) do this sort of thing. Exhibit A, randomly picked source file inside CLISP:

http://sourceforge.net/p/clisp/clisp/ci/default/tree/src/clo...

Some of Xah's indentation is inconsistent:

   (function
    (one space)
    indent like data)

   (function
     (two space indent)
     like code))
Not too much stands out otherwise; the code is readable.

I'd have to read deeper to see whether things that are usually done one way are done differently for no good reason. What I would consider "unidiomatic" would be, for instance:

  (if (not (null list)) ...)
rather than

  (if list ...)
(Any Lisp dialect requiring code such the former is labelled differently, namely "idiotic".)


I perused a couple of links. This code [0] is pretty much unidiomatic. Closing parens one per line and using setq all over the place.

[0]: http://ergoemacs.org/emacs/elisp_grep_string_inside_tag.html


hi PuercoPop, that code is meant to be run as a script (emacs --script filename), so i used setq is possibly more proper. But, this is just post-fact defense, as lots my pages began as quick blogs. As you know, CL has controversial status in gnu emacs dev community. e.g. http://ergoemacs.org/emacs/elisp_common_lisp_in_emacs.html I myself avoid it, because i don't like CL. I'm going to tuck in the hanging parens in a few days. Thank you for the criticism. (thanks to many others here too)


But stuff like

   (let (var1 (var2 0))
     (when condition
       (setq var1 (var1-initializer-form))
        ...)))
is just silly. Only the inner scope of the when uses var1, or any of the vars; it could just be:

   (when condition
     (let ((var1 (var1-initializer-form))
           (var2 0))
        ...))
Never do two-step initialization of a variable without an excellent reason; it's just sloppy otherwise.


Using lisp to while/set... sigh. This is old age emacs lisp. We need more dash.el. Unfortunately we'd need a more stream/functional buffer API ? I was thinking of porting scheme regexp SRFI because it felt a better fit than re-search-*.


As far as dash.el goes, I find lispm's critique of dash.el pretty convincing[0]. However a more profilic emacs coder fried of mine is undeterred and keeps using modern emacs lisp. I think much of the community does as well.

[0]: https://www.reddit.com/r/lisp/comments/29ufoc/it_is_not_hard...

About Scheme's regexp SRFI, have you seen rx.el? Not quite but I like it myself.


IIUC rx.el is a sexp dsl/interface to build vanilla regex down the line right ? from what I remember about scheme SRFI is not the 'syntax' but the return values, you get streams of match, then you can leverage, folds and maps.

lispm point is nice (and no one can argue about evaluation order and count), but beside the battle-tested aspect, `loop` scares the crap out of me. That's even more a reason to prefer things that avoids it in my mind, mentally small building blocks and patterns versus huge macro-based sublanguages. Yes general and probably super-efficiently implemented, but .. nah.


See this Xah Lee Elisp code:

    (defvar xah-left-brackets '("(" "{" "[" "<" "〔" "【" "〖" "〈" "《" "「" "『" "“" "‘" "‹" "«" )
      "List of left bracket chars.")
    (progn
    ;; make xah-left-brackets based on xah-brackets
      (setq xah-left-brackets '())
      (dotimes (x (- (length xah-brackets) 1))
        (when (= (% x 2) 0)
          (push (char-to-string (elt xah-brackets x))
                xah-left-brackets)))
      (setq xah-left-brackets (reverse xah-left-brackets)))

    (defvar xah-right-brackets '(")" "]" "}" ">" "〕" "】" "〗" "〉" "》" "」" "』" "”" "’" "›" "»")
      "list of right bracket chars.")
    (progn
      (setq xah-right-brackets '())
      (dotimes (x (- (length xah-brackets) 1))
        (when (= (% x 2) 1)
          (push (char-to-string (elt xah-brackets x))
                xah-right-brackets)))
      (setq xah-right-brackets (reverse xah-right-brackets)))
In CL this is just:

    (defun every-nth (seq start n &aux (len (length seq)))
      (loop for i = start then (+ i n)
            while (< i len)
            collect (elt seq i)))

    (defvar xah-left-brackets  (every-nth xah-brackets 0 2))
    (defvar xah-right-brackets (every-nth xah-brackets 1 2))
Write useful building blocks. Don't duplicate code. Get rid of low level imperative code.

Bonus points for a more efficient version for lists.


In TXR Lisp:

  $ txr -i
  1> (defvarl brackets '("(" ")" "[" "]" "<" ">")))
  brackets
  2> [brackets (range 0 : 2)]
  ("(" "[" "<")
  3> [brackets (range 1 : 2)]
  (")" "]" ">")
range generates lazy list of integers with a step size (the colon symbol defaults the second upper bound argument so the list is infinite). A list of integers can be used in the brackets notation to select elements. Put the two together and you have spaced selection.

Regarding defvarl, there is defvar too; defvarl means that the symbol is not marked special and so rebindings are lexical. Often, it is overkill for a global variable to support rebinding. We just pollute the symbol with the special marker and then have to namespace it or put earmuffs on it just in case, even though it is ever used that way.


Oh I wasn't arguing that CL loop was a far better solution than while loops (unless you process too long lists). I just became very very+ fond of academic recursive style. Here's a singly-linear variant for fixed steps.

    (defun ab (l)
      "I'm too lazy to reverse the result back."
      (defun _ (l a b f)
	(if (null l)
	    (if f (list a b) (list b a))
	  (_ (cdr l) b (cons (car l) a) (not f))))
      (_ l nil nil t))

    (ab '())
    (ab '(1))
    (ab '(1 2))
    (ab '(1 2 3 4 5 6 7 8 9 10))
    ;; ((9 7 5 3 1) (10 8 6 4 2))
    (ab '(1 2 3 4 5 6 7 8 9 10 11))
    ;; ((11 9 7 5 3 1) (10 8 6 4 2))

I don't know how cute or ugly a solution for variable step could be though. `ab` is a nice partitioning building block, less general than every-nth surely.

ps: Straight from the land of wishful lisping

    (defun abn (l n)
      (defun ono (n)
	"builds a cons oo cycle of size n"
	'unicorn)
      (defun noo (c)
	"rotates a cons cycle by one"
	'unicorn)
      (defun cooons (c v)
	"cons v to the head of c"
	'unicorn)
      (defun _ (l c)
	(if (null l)
	    c
	  (_ (cdr l) (noo (cooons c (car l))))))
      (_ l (ono n)))


Some Schemes - most notably Racket, which I use the most - allow for nested defines. Your code actually is a valid Racket code after polishing the differences in naming and syntax a bit:

    #lang racket

    (define-syntax-rule (defun name (arg ...) . body)
      (define (name arg ...) . body))
    (define nil null)
    (define t #t)

    (defun ab (l)
      ;; "I'm too lazy to reverse the result back."
      (defun _ (l a b f)
        (if (null? l)
            (if f (list a b) (list b a))
            (_ (cdr l) b (cons (car l) a) (not f))))
      (_ l nil nil t))

    (ab '())
    (ab '(1))
    (ab '(1 2))
    (ab '(1 2 3 4 5 6 7 8 9 10))
    ;; ((9 7 5 3 1) (10 8 6 4 2))
    (ab '(1 2 3 4 5 6 7 8 9 10 11))
    ;; ((11 9 7 5 3 1) (10 8 6 4 2))
...and you don't need to worry about recursion limits thanks to mandatory TCO.


Yeah, I spent too much time on scheme classes about recursive logic while using emacs lisp at the same time, hence the code above.


Nested DEFUNs? Not in Lisp. In Common Lisp use LABELS for recursive local functions.

I'd avoid recursive code like that as much as possible.

I'd write:

    (defun ab (l)
      (loop while l collect (pop l) into a
            while l collect (pop l) into b
            finally (return (list a b))))


Oops, I forgot defun affected the global namespace.

Your last example is nice. I had to read the CLHS loop to have a clue about linearity of multiple termination clauses though :)

ps: funny, while googling I even found some lisp checker with a rule about nested defuns (## define-lisp-pattern nested-defuns)

http://www.cs.northwestern.edu/academics/courses/325/program...


That is pretty atrocious. All the initializing setq's could just be init forms in the let.

Now you might think that this is also unidiomatic:

  (setq totalCnt (1+ totalCnt))
Turns out, though, that Elisp doesn't have modify macros, except in the CL compatibility package.

What Xah is doing is the same as the accepted SO answer for incrementing a local variable:

http://stackoverflow.com/questions/6858894/how-to-increment-...


First I forgot to add I Xah Lee's ergo emacs was a valuable resource when learning Emacs. AFAIR it succintly explained emacs base abstractions.

On the setq, I what I meant was iterating through setqs and while (and I seem to recall even catch/throw) as agumonkey mentioned.

Then again, 'in my mind', idiomatic elisp uses cl-lib :D. so (cl-incf total-count) would be better. snakeCase is unidiomatic as well.


> (Any Lisp dialect requiring code such the former is labelled differently, namely "idiotic".)

Yeah, but for idiots like me who sometimes make mistakes, having a type system that catches errors for me helps sometimes. There are plenty of times in languages that treat null as false-y where I've put completely the wrong variable in and gotten no useful feedback from my interpreter because it type-checks just fine and the error doesn't become apparent until 50 lines of code later in a completely different function.

Besides, if you're frequently writing code like this, it's probably because you don't understand what you're doing and are working at the wrong level of abstraction for your problem. If you're recursing over items in a list, it's almost certain you should be using a higher-order function.


I'd say that inexperienced Lisp programmers are likely to be classed as inexperienced based on far deeper issues than formatting. The sort of things that keep code from running correctly or at all. YMMV.


why should we, spend the time to educate each other about formatting, and spend the thousands hours semi-manually do so perhaps with help of paredit, when we could have it to automatically and transparently?

This is sort of funny coming from a guy who isn't using the shift key properly. Language conventions make communication easy. It's why $_ should be used. It's why "ain't" isn't a word and the subject comes before the verb.


Can you recommend a tutorial that uses idiomatic Emacs Lisp and covers both "why" and "how" of Emacs Lisp at a level appropriate for someone who's used CL and Emacs a lot but has never noticed that he needs ELisp?


I've also used CL and Emacs a lot; only very recently I've read both GNU Emacs Lisp manuals (introduction and reference). I think as a CL user in a couple of hours you can get an idea of what you can do, and more or less how.


The manuals built in to Emacs are good, check M-x info.


I much prefer "An Introduction to Programming in Emacs Lisp", which is available both from inside emacs in M-x info, and on the web at https://www.gnu.org/software/emacs/manual/eintr.html


OP's link covers more topics but I prefer Steve Yegge's emergency elisp for quick and dirty things.

http://steve-yegge.blogspot.com/2008/01/emergency-elisp.html


"Each page is self contained." Allright, I guess it's an Emacs site then.


T'wer we speaking of Python not Emacs, the "one true way" idiom would be deemed virtuous.


Xah Lee can't program a line of Lisp on his own. Most of that code is collected from elsewhere.

There are much better resources to learn Emacs Lisp from.


I have no desire to get into a personal argument, but I would like to point out that one contribution he has made for me that have not seen elsewhere is that he has chosen to use Emacs as a text-processing scripting environment in preference to Perl or Python or some other scripting languages. Without commenting on issues of style or idiom I found his write-ups of this experience using the text-editing primitives of Emacs to be useful and interesting.

For example, consider:

"Text Processing: Emacs Lisp vs Perl" http://ergoemacs.org/emacs/elisp_text_processing_lang.html

"How to Write grep in Emacs Lisp" http://ergoemacs.org/emacs/elisp_grep_script.html


I would avoid this stuff. His language advocacy is not very convincing and he also fails to understand what grep does.


OMG, he actually loads all the files to temporary buffers and calls it `grep`?

And the other text is misguided for the most part and reveals a misunderstanding of some basic concepts. tumba, please don't use these texts. I second lispm here. Only read these once you have a solid understanding of described concepts yourself.


emacs lisp cannot load one line of a file. It's whole or nothing.


The way to handle an unhelpful resource is to flag it. Even better would be to submit superior resources. The ad homenim is unproductive because it just makes people dumber and makes mean behavior look normal.


Xah Lee has trolled Lisp groups for years. It's well known that much of his 'advice' is useless. The number of bullshit posts by him is a bignum. His comp.lang.lisp trolling is legendary.

Nothing 'ad homenim' or mean - just saying how it is.


As an emacs beginner, I've landed on his site numerous times, often finding it helpful. The fact that he might or might not have trolled some mailing list is of no concern to me.

If you have a better alternative resource, please present it. If you can find technical faults in this resource, you should provide them so the author can correct it and other might learn of them as well.

Otherwise, stop trying to justify ad hominem with meaningless retorts.


> The fact that he might or might not have trolled some mailing list is of no concern to me.

It's not meaningless to me.

> If you can find technical faults in this resource, you should provide them so the author can correct it and other might learn of them as well.

We had extensive discussions with him for years. Literally thousands of posts can be found on comp.lang.lisp.

The only Lisp he learned was some imperative crappy style, while lecturing everyone how dumb they are.


> The only Lisp he learned was some imperative crappy style

Not to be trolling myself, but I believe that's part of the "practical" aspect.

You'll find much of the same in "Practical common lisp".


What I think of is Lisp code from the 60s, before structured programming, functional programming, etc.

when he writes:

    (let (a b c)
      (setq a ...)
      ...
      (setq b ...)
      ....)
That's basically the style of the past:

     (prog (a b c)

       (setq a ...)

       ...)
With one difference: then it was usual to program control flows in Lisp with labels and gotos...


which way to use let is just a personal taste.

When i begin in elisp, i ALWAYS stick with this form of using local var:

    (let (x y z)
     (setq ...)
     (setq ...)
     (setq ...)
     ...
    )
This makes it easier to read all local variable names. Especially when readers are beginners, non-professional programers, scientists, writers.

If we always use this simple style of let, what possible problem can it create?

• Does it creates algorithmic problem? • Does it slow down programs? • Does it violate some computer science principles? • Are there science based proof, or statistics, that shows this style does some damage, such as more difficult to maintain?

The only thing i can think of, is a matter of esthetics.

later on, sometimes i use this form

    (let (x (y 3) (z 4)) body)
with the condition that, if i set the variable in the let parameter, it must be constant. The value never changes in the body. And now sometimes i also use the (let* ...) form.

These variations are just sugar syntax. Not really important. It's a bikeshedding problem.

The thing is, in emacs lisp, there's no way to declare constants. And also, it is unnatural to code elisp or even Common Lisp without lots of setq or setf or similar.

In JavaScript, the issue is much worse. JavaScript Name Hoisting and One-Liner Functional Style http://xahlee.info/js/javascript_name_hosting_and_one-liner_...


That in no way excuses being mean.


> If you can find technical faults in this resource, you should provide them so the author can correct

That's the problem, actually - I never saw Xah correct anything he thought "right", no matter how many people presented rational arguments. Granted, I don't follow his writings that much, so maybe it happens; however my general impression is that it's utterly impossible to convince Xah of anything.

On the other hand, his site does provide a certain amount of information. It just mixes good information with bad in a way which makes it very hard for beginners to tell one from the other.


His behavior on Usenet doesn't excuse anyone's behavior on HN.


> It's well known that much of his 'advice' is useless. The number of bullshit posts by him is a bignum.

I wanted to check this. How easy would it be to find something trolling-like, written by Xah? Turns out it took only 3 tries when searching for "Xah Lee" on googlegroups comp.lang.lisp. The third post I clicked revealed this gem:

    there's a good solution to lisp's non-functional ways. 

    BAN lispers from using list or cons. Everything should be vector/array 
    instead. 

    everytime a cons is involved, lispers should get a electric shock. 

    that will immediately fix majority of lisp's non-functional programing 
    in practice. 

    though, i'll have to say, the more i read about Clojure, the better it 
    seems. It is very functional, the savior of the lisp name. 
...I wasted a bit of time on this and I'm not sure it was worth it, but I'm pasting this here to strengthen lispm argument by replacing "it's well known" with a concrete example.


con shouldn't be used, especially today. see this Guy Steele article: Guy Steele on Parallel Programing: Get rid of cons! http://xahlee.info/comp/Guy_Steele_parallel_computing.html “Get rid of cons!” is the exact words from his paper.


Bullies always rationalize their bullying by arguing their victim deserved it. Their flunkies are recognized by their chorus of "Yeah he deserved it" noises.


No, it's not about him at all.

It's just that other people do deserve to know that investing their time into studying some kind of material is not likely to pay off.

I agree, though, that "he's a troll" is not the best way of communicating it. You'll notice that I wasn't the one who did that.


When learning Emacs Xah Lee's works has been really helpful, and I'd like to thank him. If someone think Xah's wrong they have to produce better ressources.


what a malicious lie. I have written some 10 packages, at least hundred thousand lines of emacs lisp since 2005, all public in github or on my website. http://ergoemacs.org/emacs/xah_emacs_modes.html

the only borrowed code i can think of now, now named xah-extend-selection in xah-fly-keys, by Nikolaj Schumacher, fully credited in the inline doc still. (and that function is also in ergoemacs-mode, fully credited still, now lead by Matt F.)

Any code, that possibly may be considered borrowed, i'm painstaking to give credit, often taking sometimes a hour to find the person's REAL NAME correctly (as opposed to nicknames) (if public), personal site url or blog url or social network url if any, to link to, painstakingly ask the which url he prefers, and often ask permission too when it is clearly not necessary. One can find lots of “i learned this from xyz” in my website.

I learned emacs lisp, starting in 2005, the first 5 years with much help from comp.emacs or gnu.emacs.help newsgroup, and freenode's irc emacs channel, and emacs wiki. This is also fully credited. Xah's Emacs Tutorial: Acknowledgment http://ergoemacs.org/emacs/thanks.html linked at the bottom of my emacs tutorial.

I know lisp before. Read entire Scheme r4rs and 75% of SICP in 1998, 1999. These can be verified in newsgroup. I coded Wolfram Language for 6 years before 1999, and worked at Wolfram Research for half a year. I have several eassys documentinging these facts. For example: Xah Lee's Computing Experience (Impression Of Lisp from Mathematica) http://xahlee.org/PageTwo_dir/Personal_dir/xah_comp_exp.html

personally, i generally don't borrow code, because i'm rather one of those weird or aloof guy with meticulous control of things, down to every character placement. In my elisp life since 2005 to today, i must have stolen ideas, concepts. I can't recall explicitly which at the moment, but i can say in good faith that any non-trivial concepts i've used in my project i've given acknowledgement.

Note: i've been considered a troll in comp.lang.lisp and other newsgroups from about 2000 to 2010. There were many heated arguments, and there are some who will say negative things about me wherever my name is mentioned. I do not think of myself as a troll, and have written many essays on this. Netiquette Anthropology: a Tale of Tech Geekers http://xahlee.info/Netiquette_dir/troll.html

PS unrelated but instead of a separate post: I want to thank many people who have helped a year ago. That was a big help. Thank you. I do dish-washing part-time, and am ok.


I am truly glad to hear that things have improved in the past year. Your site clearly demonstrates your integrity and good will. My deep thanks for your many contributions that make the internet better.


Don't pay too much attention to the haters. Your pages usually covers a topic in a much more self contained manner than the official manual, which I find hard to use in practice.

Your pages also always contain instructive examples, something which cannot be said for the manual itself.

There's a reason your pages always show up when people search for anything emacs. People in general find them helpful.

And that's what should count.


seems like an unnecessary personal attack


It's just the truth. I've seen him posting code for a long time.

He has posted lots of bad advice, because he does not understand much Lisp programming and makes claims about all kinds of random stuff.


As someone who comes across his writings once in a while I wish you would counter his bad advice with your good advice, rather than simply write that its bad.


That's a fulltime job. comp.lang.lisp did that several years.

Just take from this discussion that his Lisp style is non-idiomatic and he writes in some primitive crappy imperative style. See my other example in this discussion.


What are some good ones? I'd love to have something to suggest to the elisp-curious.


An Introduction to Programming in Emacs Lisp is good, and is available both from inside emacs in M-x info, and on the web at https://www.gnu.org/software/emacs/manual/eintr.html




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: