Being a Clojure addict, I guess I have to leave the obligatory link to Babashka too then: https://github.com/babashka/babashka (Native, fast starting Clojure interpreter for scripting)
Yes and no. Babashka itself is Clojure/JVM program compiled with graalvm to a native binary that starts very fast. Babashka contains an interpreter for a simplified variant of Clojure. It also contains a predefined set of classes of the JVM and a predefined set of Clojure libraries.
Basically, it contains most of what you would need to use it for simple scripts, make network requests, parse command line arguments and many more things.
But, it won't run every possible Clojure program and it won't be as performant as Clojure on the JVM if the program runs longer.
As a CL addict, this isn't unlike Babashka: fast-starting CL scripting with batteries included. https://github.com/ciel-lang/CIEL (alpha) (otherwise the solution is to build a binary)
As an Emacs addict, Emacs can do it all that babashak & ciel do, plus much more than so, like having a great stepper (edebug) and possibility to view the output while stepping through the code in a separate file buffer, as well as a server for long term processes, that can watch files and directories etc.
Gauche is my favorite Scheme for general scripting. Huge Python-esque standard library, but very nicely organized. Fast enough for anything I need it to do. Straightforward command line interface. Good, mostly clear docs. I find that Guile and Chicken are harder to work with and have less of what I want available for immediate use.
Are there any Lisps that are particularly bad for scripting? Elisp comes to mind since it's not great unless you're already inside Emacs, but if you are it's pretty good.
Elisp is actually exceptional for scripting :). You can also compile Emacs without graphics for even faster startup, check emacs-nox, and you can run it as a script: https://www.emacswiki.org/emacs/EmacsScripts.
What you get extra is a really good debugger (edebug), and text processing library extensions so you don't need to start extra processes (grep, awk, tr & co) just to extract some text from one output and send it to another.
They also have really, really good integration with system, almost on par with Bash. So despite Lisp being more verbose than a DSL like Bash script, you win tons by just having a debugger. Completion and Emacs auto-typing features (snippets, abbrevs, autoinsert etc) compensate for the verbosty.
Whenever I think of Lisp for scripts, or even a login shell, I drift towards an elegant handling of pipes. Mostly because for 99% of what I do, it involves pipes.
Scsh did a lot of work with this but it’s still (as I recall) ungainly. Not to mention the idea of mixing process calls with native functions.
Indeed. However you can achieve things like that in an ordinary Lisp via unevaled (special) forms, with other words, macros. In Emacs Lisp eshell does it too, though they have extra advantage since Emacs has built-in text editor and text processing extensions. Eshell implements both Bash and Lisp syntax. I am not sure if "sharp lisp" does anything special for scripting than any other Lisp, but it is possible to do.
Indeed; that is my objection to the loop and format in CL.
But on the pragmatic side, if you want to interop with shell, or some other language/runtime, isn't it sometimes easier and less typing and noise if a program can be expressed in DSL or as a mix of Lisp and a DSL than if everything is written in pure s-expressions?
It certainly is much more convenient than Python for writing scripts that must execute other programs.
Many years ago, I have written quite a large amount of big scripts in scsh, and it was much easier to avoid bugs and to maintain them than in a traditional UNIX shell, without being so verbose as something like Python.
Unfortunately, nowadays scsh is quite ancient and it would need some serious work to bring it up-to-date, to be able to work without limitations on modern computers. It would probably be necessary to port it to a more recent Scheme interpreter than Scheme 48 (which is an old 32-bit program).
Sharpscript holds immense promise as a Lisp-based scripting language. While it may not have the extensive libraries and community support of some established languages, its unique blend of Lisp's elegance and scripting's practicality makes it a compelling choice for developers looking to explore a new realm of scripting possibilities. I'm eager to see how the language evolves and gains popularity among developers and scripting enthusiasts.
It is not as trivial as you think, but it is possible. M-x in Emacs does it. But it is certainly not as simple as "just remove start and end parenthesis" :-). In a Lisp-2 if you type a symbol "foo" without parens, you are asking for symbol foos value as a variable, but it you type (foo) you are asking for symbol foos value as a function. Since every symbol in a Lisp-2 language (Common Lisp, Emacs Lisp some other possibly) can have both variable value and function object assign to it, how would system know which one you ask for?
Anyway, it is possible to do, for example M-x in Emacs does it. They do it by means of restricting repl to execute only functions, and they limit that to special function with so-called "interactive" form, whose job is to provide function arguments.
Hah! Why am I not surprised to see that nick on HN too :-)
I have no experience with LispWorks whatsoever, but I am curious, how did they implemented it if you know? I mean, how do they differ between calling first as a value or first as a function? In your example first could be a defvared to have some value, and then I might have called a function to produce some unrelated output. I mean it is a contrived example, and probably nobody types in a repl like that, but the language does not forbid it since white spaces are insignificant.
If they just treat the first token as operator always, than they could call any function with any arguments:
CL-USER> op arg1 arg2 ... argN
but then they would have to eval all values at the start of repl by calling (symbol-function arg), instead of just type arg to examine a value. I don't think it would be a bad think in itself tbh; but I am not sure the win is big enough to introduce a new concept.
By the way, I think I have stumbled on some mailing list comment somewhere once, where they said, that Hemlock can use commands named more "english like", i.e. they can have names with spaces in it, not just the hyphenated function name as in Emacs. Do you know more about it, or how they have implemented it? I am just curious; seen your comments on Reddit, and if you are the same person, I understand you are well experienced and familiar with various Lisps and implementations; hope you don't mind asking. :)
> Hemlock can use commands named more "english like"
That's like Emacs (called Zmacs) on the Lisp Machine does it. It does not use hyphens at all. I can't remember how the first Emacs, the TECO Emacs, did it.
M-x compile-changed-definitions-of-buffer
is then
M-x Compile Changed Definition Of Buffer
Basically instead of hyphens it uses spaces.
In LispWorks & Zmacs one enters the command M-x Replace String <return> and then the editor prompts for the arguments.
But then they do have the problem I mentions: they can't distinguish between calling (foo 1 (+ 1 2) (+ 3 4)) and calling (foo 1) (+ 1 2) (+ 3 4).
I don't think it is important ether, I certainly wouldn't type code like that in a repl either. I don't even know if sly/slime repl even support typing several expressions on the same line in repl. I am not at home to test it.
> Basically instead of hyphens it uses spaces.
>
> In LispWorks & Zmacs one enters the command M-x Replace String <return> and then the editor prompts for the arguments.
I understand, thanks.
I thought they were a bit more finicky about it. I think that could be relatively easily done in Emacs as well, but I don't see much gain to implement a M-x replacement like that. Especially with modern completion systems like Helm and fuzzy completing which basically don't care how we type.
> they can't distinguish between calling (foo 1 (+ 1 2) (+ 3 4)) and calling (foo 1) (+ 1 2) (+ 3 4).
It uses always (foo 1 (+ 1 2) (+ 3 4)). It uses the complete input.
> I don't even know if sly/slime repl even support typing several expressions on the same line in repl.
Most Lisp REPLs use multiline input. Here LispWorks requires that + 1 2 is on a single line. If one enters more than one expression to a single prompt, possibly on/spanning multiple lines, each will be executed.
> but I don't see much gain
It looks better. Much better.
Minus: the command now has spaces in its name, which makes it more difficult to use in scripts.
I don't think it would be difficult to implement in Emacs, by just transforming the symbol name and presenting that to user instead. I think the read-extended-command-1 is the place where they actually do the work. They could also store a display string for each command in symbols plist as they do with interactive form, but it would be more surgery and as you say harder to use in the code. If display string is decoupled from the function name than the programmer has to remember as well the name for the command, but if they just do a transformation than the programmer can remember the rule. We are used to this in menus and gui stuff like that indeed, labels are often different strings than actual symbol names used for function callbacks, but I am not sure if we would prefer that on the command line.
In my opinion there is still discrepancy between the spoken language and the function names. I guess that is why people have come up with llamas and what not they call those AI llms and chatgpt and so on. I personally am not so fun of using spoken languages as a programming language or a way to control the computer, but who knows, perhaps one day M-x will run some llm search instead of precise lisp function names.
That was an ambitious error recovery in Interlisp indeed. It took like 30 ~ 40 years to get similar thing in Emacs, and we still don't have the "trusted" mode, just cautious with flycheck & co.
> For display it removes the com and the hyphens.
In Emacs, packages are supposed to define a group node and a prefix for symbol names for Customize. Not all packages do, but most of quality ones do. We could actually use that prefix and implement similar strategy to display labels for menus and commands. Menus in Emacs are also implemented as keymaps, but they do require a programmer to specify a label for display in "easy menu" as they call their menu implementation. This strategy could thus remove the need for that extra label.
DWIM from Interlisp could be implemented in forms too. All forms have to use known symbols unless in some well-defined places: literals (strings, quoted symbols), the symbol name which is the word after the certain operator (defun, defmacro, defalias, etc), in argument lists, or as a first symbol of locally defining symbols (let, let*, flet, labels and similar). But there are some questions I am not sure how to answer: I think I will have to tell the system somehow that certain part of an expression is a "defining" part so it understands which symbols are to be introduced into the system and not checked or mistaken for misspelled.
It would be actually useful to have such feature, bound to space and return, so when we type it just replaces misspelled symbols. I am a master of misspellings, I often replace two characters place for some reason, like in Siebel as seen :).
> they can't distinguish between calling (foo 1 (+ 1 2) (+ 3 4)) and calling (foo 1) (+ 1 2) (+ 3 4).
The former is encoded as
> foo 1 (+ 1 2) (+ 3 4)
and the latter is three separate top-level forms, encoded as
> foo 1
> + 1 2
> + 3 4
Here it looks as if I'm assuming we have one-liners, but that doesn't have to be so; there could be a way to insert soft line breaks that don't dispatch the expression:
> foo 1
(+ 1 2)
(+ 3 4)
> foo 1
> + 1
2
> + 3
4
You do have to restrict the input to one form. Whereas a REPL which uses regular notation can handle a single input line of (foo 1) (+ 1 2), and do two evaluations, we cannot have that here. This style of REPL basically inserts parentheses for you, and it inserts one set of them around your whole input, which makes it a single form.
Yes, well that is basically what I meant. I guess in practice, those examples I made are contrived, so I suppose what they do is not a problem for the most people in practice, so I don't think it is a big argument against.
40 comments
[ 3.1 ms ] story [ 90.1 ms ] threadhttps://janet-lang.org
Is this something I could use as an alternative?
Basically, it contains most of what you would need to use it for simple scripts, make network requests, parse command line arguments and many more things.
But, it won't run every possible Clojure program and it won't be as performant as Clojure on the JVM if the program runs longer.
Or scsh, for the old school: https://scsh.net
https://stackoverflow.com/search?tab=newest&q="txr"%20is%3aa...
https://unix.stackexchange.com/search?tab=newest&q="txr"%20i...
https://www.gnu.org/software/emacs/manual/html_mono/eshell.h...
What you get extra is a really good debugger (edebug), and text processing library extensions so you don't need to start extra processes (grep, awk, tr & co) just to extract some text from one output and send it to another.
They also have really, really good integration with system, almost on par with Bash. So despite Lisp being more verbose than a DSL like Bash script, you win tons by just having a debugger. Completion and Emacs auto-typing features (snippets, abbrevs, autoinsert etc) compensate for the verbosty.
Scsh did a lot of work with this but it’s still (as I recall) ungainly. Not to mention the idea of mixing process calls with native functions.
Something like this would be nice:
This answer by Jörg W Mittag is very convincing:
https://stackoverflow.com/a/3640403/512904
This table in particular is very illuminating:
Here's a similar table with add your example's syntatic elements:But on the pragmatic side, if you want to interop with shell, or some other language/runtime, isn't it sometimes easier and less typing and noise if a program can be expressed in DSL or as a mix of Lisp and a DSL than if everything is written in pure s-expressions?
https://scsh.net/about/what.html
It certainly is much more convenient than Python for writing scripts that must execute other programs.
Many years ago, I have written quite a large amount of big scripts in scsh, and it was much easier to avoid bugs and to maintain them than in a traditional UNIX shell, without being so verbose as something like Python.
Unfortunately, nowadays scsh is quite ancient and it would need some serious work to bring it up-to-date, to be able to work without limitations on modern computers. It would probably be necessary to port it to a more recent Scheme interpreter than Scheme 48 (which is an old 32-bit program).
If you want multiline, you can always type the paren yourself.
To remind the user, make the default prompt `( `
Also consider using brackets [] instead of parens to be really wrist friendly.
Anyway, it is possible to do, for example M-x in Emacs does it. They do it by means of restricting repl to execute only functions, and they limit that to special function with so-called "interactive" form, whose job is to provide function arguments.
I have no experience with LispWorks whatsoever, but I am curious, how did they implemented it if you know? I mean, how do they differ between calling first as a value or first as a function? In your example first could be a defvared to have some value, and then I might have called a function to produce some unrelated output. I mean it is a contrived example, and probably nobody types in a repl like that, but the language does not forbid it since white spaces are insignificant.
If they just treat the first token as operator always, than they could call any function with any arguments:
but then they would have to eval all values at the start of repl by calling (symbol-function arg), instead of just type arg to examine a value. I don't think it would be a bad think in itself tbh; but I am not sure the win is big enough to introduce a new concept.By the way, I think I have stumbled on some mailing list comment somewhere once, where they said, that Hemlock can use commands named more "english like", i.e. they can have names with spaces in it, not just the hyphenated function name as in Emacs. Do you know more about it, or how they have implemented it? I am just curious; seen your comments on Reddit, and if you are the same person, I understand you are well experienced and familiar with various Lisps and implementations; hope you don't mind asking. :)
It's relatively easy:
a variable:
CL-USER> foo
an operator with no arguments:
CL-USER> (foo)
an operator with one or more arguments:
CL-USER> foo 1
CL-USER> foo 1 2
CL-USER> foo 1 (+ 1 2) (+ 3 4)
and so on.
> Hemlock can use commands named more "english like"
That's like Emacs (called Zmacs) on the Lisp Machine does it. It does not use hyphens at all. I can't remember how the first Emacs, the TECO Emacs, did it.
M-x compile-changed-definitions-of-buffer
is then
M-x Compile Changed Definition Of Buffer
Basically instead of hyphens it uses spaces.
In LispWorks & Zmacs one enters the command M-x Replace String <return> and then the editor prompts for the arguments.
But then they do have the problem I mentions: they can't distinguish between calling (foo 1 (+ 1 2) (+ 3 4)) and calling (foo 1) (+ 1 2) (+ 3 4).
I don't think it is important ether, I certainly wouldn't type code like that in a repl either. I don't even know if sly/slime repl even support typing several expressions on the same line in repl. I am not at home to test it.
> Basically instead of hyphens it uses spaces. > > In LispWorks & Zmacs one enters the command M-x Replace String <return> and then the editor prompts for the arguments.
I understand, thanks.
I thought they were a bit more finicky about it. I think that could be relatively easily done in Emacs as well, but I don't see much gain to implement a M-x replacement like that. Especially with modern completion systems like Helm and fuzzy completing which basically don't care how we type.
Thank you for your time and answers.
It uses always (foo 1 (+ 1 2) (+ 3 4)). It uses the complete input.
> I don't even know if sly/slime repl even support typing several expressions on the same line in repl.
Most Lisp REPLs use multiline input. Here LispWorks requires that + 1 2 is on a single line. If one enters more than one expression to a single prompt, possibly on/spanning multiple lines, each will be executed.
> but I don't see much gain
It looks better. Much better.
Minus: the command now has spaces in its name, which makes it more difficult to use in scripts.
> It looks better. Much better.
I think it is just preference.
I don't think it would be difficult to implement in Emacs, by just transforming the symbol name and presenting that to user instead. I think the read-extended-command-1 is the place where they actually do the work. They could also store a display string for each command in symbols plist as they do with interactive form, but it would be more surgery and as you say harder to use in the code. If display string is decoupled from the function name than the programmer has to remember as well the name for the command, but if they just do a transformation than the programmer can remember the rule. We are used to this in menus and gui stuff like that indeed, labels are often different strings than actual symbol names used for function callbacks, but I am not sure if we would prefer that on the command line.
In my opinion there is still discrepancy between the spoken language and the function names. I guess that is why people have come up with llamas and what not they call those AI llms and chatgpt and so on. I personally am not so fun of using spoken languages as a programming language or a way to control the computer, but who knows, perhaps one day M-x will run some llm search instead of precise lisp function names.
In Genera the command "Find Symbol" has com-find-symbol as a symbol name. For display it removes the com and the hyphens.
> For display it removes the com and the hyphens.
In Emacs, packages are supposed to define a group node and a prefix for symbol names for Customize. Not all packages do, but most of quality ones do. We could actually use that prefix and implement similar strategy to display labels for menus and commands. Menus in Emacs are also implemented as keymaps, but they do require a programmer to specify a label for display in "easy menu" as they call their menu implementation. This strategy could thus remove the need for that extra label.
DWIM from Interlisp could be implemented in forms too. All forms have to use known symbols unless in some well-defined places: literals (strings, quoted symbols), the symbol name which is the word after the certain operator (defun, defmacro, defalias, etc), in argument lists, or as a first symbol of locally defining symbols (let, let*, flet, labels and similar). But there are some questions I am not sure how to answer: I think I will have to tell the system somehow that certain part of an expression is a "defining" part so it understands which symbols are to be introduced into the system and not checked or mistaken for misspelled.
It would be actually useful to have such feature, bound to space and return, so when we type it just replaces misspelled symbols. I am a master of misspellings, I often replace two characters place for some reason, like in Siebel as seen :).
The former is encoded as
and the latter is three separate top-level forms, encoded as Here it looks as if I'm assuming we have one-liners, but that doesn't have to be so; there could be a way to insert soft line breaks that don't dispatch the expression: You do have to restrict the input to one form. Whereas a REPL which uses regular notation can handle a single input line of (foo 1) (+ 1 2), and do two evaluations, we cannot have that here. This style of REPL basically inserts parentheses for you, and it inserts one set of them around your whole input, which makes it a single form.