25 ms·
One place I get stuck all the time with Common Lisp is the REPL. I'm used to IPython, which allows me to enter multiple lines and execute them all at once, the
by ryan-duve 3y ago
One place I get stuck all the time with Common Lisp is the REPL. I'm used to IPython, which allows me to enter multiple lines and execute them all at once, then go "up" and get all those lines back and change something to fix the error:
In [1]: my_list = [1, 2, 3]
...: my_other_list = [4, 5, 6]
...: final_list = [str(i) for i in my_list ++ my_other_list] # whoops a syntax error!
...:
...: "".join(final_list)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[1], line 3
1 my_list = [1, 2, 3]
2 my_other_list = [4, 5, 6]
----> 3 final_list = [str(i) for i in my_list ++ my_other_list] # whoops a syntax error!
5 "".join(final_list)
TypeError: bad operand type for unary +: 'list'
In [2]: my_list = [1, 2, 3]
...: my_other_list = [4, 5, 6]
...: final_list = [str(i) for i in my_list + my_other_list]
...:
...: "".join(final_list)
Out[2]: '123456'
All the CL REPLs I've tried only allowing getting back one line at a time, which feels tedious to execute in order. I feel like I'm fundamentally missing something about iterative development in Common Lisp and it's blocking me from learning the language.
- smatija 3y agoWhen I last used CL using Emacs+SLIME helped a lot. Especially C-x C-e (evaluate under the cursor) and C-M-x (evaluate form you are inside of). With this you get all the benefits of classic editor together with REPL-like instant feedback. https://slime.common-lisp.dev/ https://slime.common-lisp.dev/
- pamoroso 3y agoIn a Lisp-aware editor, such as Emacs with SLIME, you can send to the REPL for evaluation an arbitrary block of text that contains multiple separate expressions. See for example M-x slime-eval-region (C-c C-r ).
- collinrapp 3y agoWhen I read “I feel like I'm fundamentally missing something about iterative development in Common Lisp” in the GP, I thought of exactly what’s in these replies. I’ve only recently started learning CL via Practical Common Lisp, and while I liked Emacs+SLIME, I’m a vim guy (I know) and switched to vim+VLIME instead, and so far I’m loving it. This to me has actually been the “secret sauce” of Lisp in my early experience, because now when I go to write code or use the REPL for languages like Python and Ruby, I find myself missing the SLIME/VLIME experience. I find it to be a very intuitive and efficient way to write code interactively.
- ryan-duve 3y agoAny chance you could drop the Common Lisp equivalent of the Python program in the original post here? That is, the code that goes in Vim, then what commands or key bindings you use to execute and find the "add two lists" syntax error.
- remexre 3y agohttps://asciinema.org/a/kvYjBNJtpKXd5NOCDogY3KyP7 https://asciinema.org/a/kvYjBNJtpKXd5NOCDogY3KyP7
- pjmlp 3y agoEven VS has send to REPL kind of experience for Python, maybe vim isn't the right tool, rather using something else.
- gpderetta 3y agoFor the record you can do the same with various emacs python modes, and it is vastly superior to using the python repl alone.
- Jtsummers 3y agoThey typically read one expression at a time. Wrap your multiline/multiexpression code in a let or progn expression or similar if you want to write multiple expressions before evaluating them. They will normally let you write as many lines as needed for an expression and then let you go back and edit the whole thing.
- pjmlp 3y agoIf you want something like IPython, you need to look into the commercial survivors from Common Lisp, with a full blown IDE experience, like Lisp Works and Allegro Common Lisp.
- susam 3y agoAre you using the CL REPL directly on a terminal emulator? I do not type expressions directly into the REPL often. I do sometimes but not often. Instead I send expressions right from the editor to the REPL. The REPL itself is available as a separate buffer in the editor. How this is done depends on the editor/IDE. Some examples: • With Emacs + SLIME, we can type C-M-x (evaluate current top-level form) or C-x C-e (evaluate the expression before the cursor). • With Vim + Slimv, it is ,e and ,d respectively. • Yet another popular option is Vim + Vlime in which case the key sequences are \ss and \st instead. I have written two guides to explain how to create such a development environment from scratch and get started with such a setup: • https://github.com/susam/emacs4cl https://github.com/susam/emacs4cl • https://susam.net/lisp-in-vim.html https://susam.net/lisp-in-vim.html There are commercial implementation + IDEs too which according to many people provide even better integrated development experience. The most popular among them is perhaps LispWorks. I use Emacs + SLIME myself.
- ryan-duve 3y agoYes, I was directly typing in the REPL. I think Slimv may have been the thing I'm missing. When I'm back to my laptop I will give it a shot. Thanks!
- lispm 3y agoI would type into a REPL, but not in a terminal. Typically one would run a REPL inside an editor or something similar. The tool to run a REPL is usually called a "Listener" in Lisp. For example I would use GNU Emacs, call SLIME and use a SLIME REPL. Lisp development in a terminal, without an editor, is not so helpful.
- ryan-duve 3y agoIt's been a week so I can't edit my other response to this, but yes, Slimv was what I was missing. I read your second link and followed the instructions and am now able to actually learn this language. Thank you for sharing. For anyone else that knows Vim but can't seem to get a good CL environment going, read https://susam.net/lisp-in-vim.html https://susam.net/lisp-in-vim.html
- neutronicus 3y agoIn Lisp you'd do something like (let* ((my-list '(1 2 3)) (my-other-list '(4 5 6)) (final-list (concatenate 'list my-list my-other-list))) (whatever-cl-calls-join final-list "")) This is a single Lisp form evaluated at the REPL that you could get back and edit as necessary. It's been a decade or so haha but that's the idea
- ogogmad 3y agoBroad question about Lisp conventions: Why can't you just define each variable in a separate line, instead of batching those definitions up at the top? This seems like a confusing break from convention. In C-derived languages, you can stick variable definitions anywhere. I've looked it up and Common Lisp doesn't seem to provide such an option. Seems like an odd restriction. Oh, and the difference between LET, LET* and LETREC is another weird break from convention.
- dwringer 3y agoYou can do each variable on its own line with stuff like "defvar" and "setf", but that will add them to the current package namespace - and convention is to avoid polluting namespaces with names that will be unused later. So, if the variables are only used within a given scope, the scope is explicitly delineated with a "let" block. It's not a general requirement. I'm not sure what you mean about the difference between LET and LET* (the latter simply lets subsequent variable declarations refer to previously declared variables in the same block), and LETREC is not a builtin part of Common Lisp.
- ogogmad 3y agoI think SETF only reassigns a variable that's already been declared. DEFVAR defines a dynamically-scoped global variable, no? So why doesn't Common Lisp let you write `(VAR new-var new-val)` like every single other language, and have it declare a variable in the current scope? Unless there's a good reason, this is yet another obstacle to these languages being adopted by anybody except the die-hards. If you want to carefully delimit scope, doesn't Common Lisp (and other Lisps) provide PROGN? Seems like a less complicated approach. > I'm not sure what you mean about the difference between LET and LET* (the latter simply lets subsequent variable declarations refer to previously declared variables in the same block Why does LET even exist as an alternative to LET*? Why does Lisp even bother making this distinction?
- behnamoh 3y agoThe sibling comments seem to interpret your need in a lispy way! In IPython, you could use arrow keys to go back in history and then edit the code you want and execute it. It seems you're saying that CL doesn't have this feature. But what the commenters say is you don't probably need this feature anyway because you can write the code you want in the editor and send it to REPL. Python's REPL has this feature too, but obviously it's not a "clean" approach because sometimes we just want to experiment with things and don't want to pollute the main code with these one-off expressions.
- kragen 3y agoi think it would be more accurate to gloss the sibling comments as saying that the place where lisps put this feature is in emacs rather than in ipython; that is, emacs is the lisp equivalent of ipython python's built-in repl also lacks the desired feature, and it's kind of a pain in the ass, but the ^o keybinding can go some distance to compensating for it; when you use ↑ or ^r to get back to a desired line in history that begins a multiline block, after editing it, type ^o instead of enter, and the next line will appear below. works in bash too, and it's super common in my experience to want to run multiple historical commands in sequence instead of just one jupyter notebook is maybe a better alternative to the emacs feature. darius bacon's halp provides a sort of notebook-like feature in emacs
- ryan-duve 3y agoI kind of figured I was approaching this wrong. If that approach was useful, I would have found someone already implemented it. One silly hangup I have is I am more comfortable in Vim than Emacs, and I am trying to isolate my learning to a language and not a language-plus-an-editor. This must be how people feel when I suggest they switch to Vim to make a certain workflow easier :-) My goal isn't really to get "IPythonButForCommonLisp", but to iteratively build up programs so I can quickly learn syntax. I'm going to go with the Slimv suggestion the next chance I get and see if that solves it. Instead of working in revisioned code, I'll probably just work in `Untitled.lisp` until I get the hang of things.
- fiddlerwoaroof 3y agoSo, I use the CL repl in emacs pretty heavily (SLIME) and M-p / M-n do the thing you’re talking about ipython doing. You can also use the arrow keys to navigate to a previous expression and hit enter to copy the whole expression to the current input.
- dhbradshaw 3y agoTo get a nice combination of easy edits and evaluation in a format familiar to a pythonista, could play with Lisp in Jupyter: https://github.com/yitzchak/common-lisp-jupyter https://github.com/yitzchak/common-lisp-jupyter
- gumby 3y agoWell, just begin your typing with `(progn ` and when you’ve typed the lines you want just press `)`. And/or run your repl inside emacs and you’ll have your whole history available right there.
- jrvarela56 3y agoI learned a trick from Clojure that helps with this (they call them rich comments). You never type into the REPL but include comment sections across your programs where you write code as it's intended to be used/executed and then use key bindings to highlight/run in your REPL. This allows you write several lines and highlight them in order to run them.
- whartung 3y agoCLISP uses READLINE, and it's "form" based, so if you have a multi-line form, an up-arrow, you get the entire form. SBCL doesn't have anything that I'm aware off, neither does CCL. I've seen mentioned of wrapping SBCL in a readline wrapper. There's a program that essentially gives readline behavior to anything that reads stdin, a readline interface. It may be name something clever like "readline", I've forgotten. I've never used it. The terminal experience is weak on those Lisps simply because of the dominance emacs has in this space. Wrap SBCL or CCL in Slime and you get readline and more. There's simply little demand for a more functional CLI when the emacs/slime combo is so powerful and useful. The burden for Slime and emacs (for this use case) is actually quite low. Both are pretty easy to install, modern emacs out of the box works with simple mouse gestures and arrow keys, so you don't need to be an emacs wonk to use it. And Slime has its own dropdown menu for most tasks. Readline in CLISP is useful, it makes CLISP orders of magnitude more useable than raw SBCL. Cutting and pasting S-Exprs is just not a great experience for routine work, IMHO. One advantage of readline over the emacs buffers is that when you up-arrow, you get the form. In the buffer, if you up-arrow you go up one line. Mildly annoying when your last form spat out a 1000 lines. (That's why you search instead, but, nit noted.) With readline your REPL experience is more like the shells. And that may all work with the readline wrapper, but the wrapper may well not be aware of S-exprs, so if you enter a multi line expression and up-arrow you may get just the last line of your last expression. Kind of worst of both world. But, I'm just supposing here, I've not used it. I'm quite content with CLISP (which does not have a lot of modern activity on it), I just wish I could get it with SSL. This seems to be some grand challenge I have not found a top-of-first-page "SSL in Clisp" solution for on google. Anyway, enough rambling. Install emacs and slime.
- mtreis86 3y ago>There's a program that essentially gives readline behavior to anything that reads stdin, a readline interface. rlwrap https://man.archlinux.org/man/rlwrap.1 https://man.archlinux.org/man/rlwrap.1
- dpflug 3y agohttps://linedit.common-lisp.dev/ https://linedit.common-lisp.dev/ can do the same for SBCL and other CLs
- lispm 3y agoLisp is not "line oriented", but "expression oriented". Lisp is a "List Processor", not a "Text Processor". If you want to group expressions in a REPL into one expression, use PROGN (or similar). The R in REPL stands for READ, which is a Lisp function, which reads an expression and returns data.