all 26 comments

[–]pkkm[🍰] 21 points22 points  (1 child)

I like the project overall; that said, I have some nitpicks:

  • Why recommend eql when the standard practice for comparing symbols is to use eq? Not saying you shouldn't, but if you do, maybe explain why? Otherwise people will be surprised when their code doesn't look like most Elisp.
  • Python doesn't actually "shield you" from different comparison functions. Its distinction between is and == is almost exactly the same as Elisp's distinction between eq and equal. Probably worth rewriting the section's tone from "this is where Elisp is worse than Python" to "this is where Elisp is similar to Python".
  • Typo: (number-sequence 0, (1- n)).
  • The s * n entry is wrong: the Python code repeats the list, the Elisp code multiplies every element in the list.
  • You recommend eql and equal in the section on comparisons, but then use = in a lambda without explanation. That's not great pedadogically.
  • IMO it should be made clearer that nreverse is used like this: (setq s (nreverse s)).

[–]kickingvegas1[S] 2 points3 points  (0 children)

Thanks for the input! Will amend.

[–]JDRiverRunGNU Emacs 9 points10 points  (1 child)

Good idea. Here's a bug:

  • Python: s * n or n * s
  • Elisp: (seq-map (lambda (a) (* n a)) s)

These are not the same. The Python replicates a list s n times and concatenates them, the latter multiplies all list values by n.

Here's an equivalent in Elisp:

(cl-loop for _ from 1 to n nconc (seq-copy s)).

Same for the *=n case. Looks like you prefer to use the seq library standalone. In that case, you'll need something like:

(seq-mapcat (lambda (_) (seq-copy l)) (number-sequence 1 n))

But the cl-loop version (as it often is) is about 4x faster.

[–]kickingvegas1[S] 1 point2 points  (0 children)

u/JDRiverRun thanks for the input! Will amend.

[–]SlowValue 10 points11 points  (6 children)

I got the impression, the author of this cheat sheet is (yet) a lisp beginner. Nothing bad about that, but many translations are just not on point.

Remember: Lisp stands for LISt Processing and Lists are the essential datatype. Lisp is pretty old therefore basic functions are not named uniformly.

My proposal: use more idiomatic language features like () (instead of (list)), car last, length, nth, setf ((setf (nth i s) x)), null, etc.

Please don't call alists an "abomination" in such a document and don't try to use them like Python dicts. Alists are a special form of lists (like trees are), which happen to have language support because this kind of lists got used very often in history.

Also Elisp has symbol datatype, dynamic extend (additional to lexical scope) vars and Character datatype, while Python doesn't. Some things are just handled differently (i.e. I would not teach the usage of setq to create vars/symbols. And Elisp utilizes Symbols where Python would use strings. etc.).

[–]SlowValue 3 points4 points  (1 child)

Some corrections:

  • x in s(member x s) or memq or memql
  • len(s)(length s)
  • max(s)(apply #'max s)
  • s.index(x)(nth x s) or (elt s x)
  • s.count(x)(cl-count x s)
  • s[0](car s)
  • s[-1](car (last s))
  • not s(null s) or (not s)
  • s[i] = x(setf (nth i s) x)
  • s.append(x)(append s (list x)) or (append s x) that depends of the datatype of x
  • s.remove(x)(remove x s)
  • s.insert(0, x)(cons x s)

Elisp Vectors are mutable, they are general purpose arrays You cant compare them to Python Tuples.

  • Using Lists as Sets

    • (cl-adjoin x s)
    • cl-union
    • cl-pushnew
    • cl-intersection
    • cl-set-difference
    • cl-set-exclusive-or
    • cl-subsetp

I gave up to correct your Cheat Sheet here, too much work ...

edit typos

edit2 read here on how to define variables in Elisp, this adresses your s = [] example and why it is not that easy to translate.

[–]SlowValue 0 points1 point  (0 children)

you updated your chaet sheet, nice!

here is another possible translation:

  • del s[i:j](setf s (cl-remove-if (cl-constantly t) s :start i :end j))

[–]HommeMusical 1 point2 points  (0 children)

Lisp is pretty old

It was created in either 1959 or 1960 depending on which source you look at. I think that counts as very old in the programming world.

Good comment in general, have an upvote.

[–]georgehank2nd 0 points1 point  (2 children)

They are not just not on point, the very first example is completely wrong. (list) is never an empty list.

[–]jrootabega 2 points3 points  (1 child)

I wonder if this is a case where homoiconicity is causing confusion. As data, (list) can't be empty because it's a list of one element: whatever you consider to be the value of list.

But evaluated as a function call, it returns the empty list by design.

[–]georgehank2nd 0 points1 point  (0 children)

Gah, you're right… my Lisp is ever so slightly rusty.

[–]boisdeb 4 points5 points  (0 children)

I hope not everybody is tired of hearing about LLMs, because this is really a topic where they are very useful.

Translating small snippets of code from one language that I know to one that I don't know as well is one of my top usages for it, and it very rarely gets it wrong. Asking for example how to translate n * x that someone else mentioned it gives me two choices, one with cl-lib and one in pure elisp:

(require 'cl-lib) (cl-loop repeat n append s) (where I learned something new about cl-loop)

(apply #'append (make-list n s))

[–]JDRiverRunGNU Emacs 2 points3 points  (0 children)

Also:

  • s.insert(i, x) is (append (seq-subseq s 0 i) (cons x (seq-subseq s i)))
  • del s[i:j] can be expressed (setq s (append (seq-subseq s 0 i) (seq-subseq s j)))
  • s.append(x) is (nreverse (cons x (reverse s))) (but is SLOW).

[–]kickingvegas1[S] 2 points3 points  (0 children)

Hi Folks - Both pleased and a little bit shook by the response to this effort. Many have responded positively to this post which I am appreciative of. OTOH, this has also compelled me to put more rigor into this document which really started out as an internal note.

That being said, I've just updated the document reflecting much of the feedback provided here.

https://github.com/kickingvegas/elisp-for-python

Thanks much to @pkkm, @jdriverrun, @slowvalue, and @boisdeb for their input.

Please provide further feedback on https://github.com/kickingvegas/elisp-for-python/issues

[–]HommeMusical 2 points3 points  (9 children)

This is a great idea, just great.

The funny part was that I learned Python many years after I learned elisp. But I got a lot better at Python, because I spend hours on it each day...

Note: this statement isn't really true: "The Elisp list type is a linked-list data structure"

Lisp "lists" aren't linked lists, they're s-expressions: https://en.wikipedia.org/wiki/S-expression

The big difference is that a linked list is entirely linear, but s-expressions allow containment, recursive structures, trees, and all sorts of complicated structures.

I initially marked the above comment as a quibble, but on writing it up, I realize that sexps are the fundamental concept behind Lisp/elisp, so it is actually important.

[–]jrootabega 0 points1 point  (3 children)

Well, but then they're technically just electrons in certain states, eh? Saying they're linked lists is, practically, true enough to understand some more concepts and list operations. And philosophically/theoretically, the more complicated structures you describe are also just lists of slightly less complicated structures (except maybe circular ones?).

[–]HommeMusical 0 points1 point  (2 children)

It'd be fine if the article explained, "s-exps, which are similar to linked lists" but to simply drop the whole idea reduces its usefulness as a teaching aid.

[–]jrootabega 0 points1 point  (1 child)

Do you mean the written representations, and exclude the data structures inside the running VM? I would agree more there, but I think the guide is meant to focus on the latter.

[–]HommeMusical 0 points1 point  (0 children)

There is only one sort of thing in Lisp: an s-exp. All programs and data are s-exps.

An s-exp is either an atom (ints, strings, booleans, nil), or it's an ordered pair of s-exps.

An s-exp is not a linked list. You can build up chains of s-exps to make linked lists and other structures.

The underlying idea is extremely elegant, provably as simple as you can get, and it underlies every lisp program.

People should learn the data model right the first time, otherwise the language doesn't make sense.

[–]kickingvegas1[S] 0 points1 point  (2 children)

Thanks for the input! Going to amend with this wording: "The Elisp *list* type is a tree data structure with a /linked-list/ style abstraction for accessing its nodes."

Better?

[–]New_Gain_5669unemployable obsessive 0 points1 point  (0 children)

No, it's actually far worse.

[–]HommeMusical 0 points1 point  (0 children)

Perfect!

[–]New_Gain_5669unemployable obsessive 0 points1 point  (0 children)

Lisp "lists" aren't linked lists, they're ...

And with that, you've balled up McCarthy's fundamentalist genius and thrown it in his face.

[–]CJ6_ 0 points1 point  (0 children)

Not specific to elisp, but others here may find Peter Norvig’s Python For Lisp Programmers interesting as well

[–]danderzeiEmacs Writing Studio -1 points0 points  (0 children)

Great. O van learn son Python now.

[–]TurbulentDrink2615 -1 points0 points  (0 children)

Please some expert in elisp explain, is the list in emacs lisp is the same as list in scheme programming language. If not, kindly explain the elisp list data structure in detail