PicoLisp it’s decades old and never awoke so much interest… it has nothing inherently wrong with it and offered quite handy solutions to many common issues.
I wonder why never got many fans in spite of the strong tries from its author to promote it.
Lisp has had some pretty decent standards for years - Common Lisp and Scheme. Both with stellar books about them. And at least two or three implementation for your particular niche (well, at least once CMUCL wasn't the only game in the free CL territory).
It's quite hard to get into that territory with different ideas, you'd have to provide a rather good value add for that. Even newLisp[1] didn't, and that both had various implementations and a standard.
Clojure did, and not just because of the JVM (cf. ABCL/Kawa).
picoLisp just never really presented itself as being particularly unique for a niche. (IIRC also didn't run on Windows)
lush is another failed lisp that really seemed like it should have gone somewhere. like picolisp and newlisp it was dynamically scoped, though, which i'm convinced limited the number of people willing to give it a good try.
Wow, Lisp ranks in the top 4/1000 of use? I didn't know we were that popular :-)
Seriously though, I think I like Common Lisp so much just because I have been using it since about 1982 and I am just very used to it. For a lot of work, I will just use Python if there are one or more Python libraries that solve whatever problem I am working on with just a few lines added Python code that I need to write.
Minimalist and based on llvm is a dubious combination. If your dependency set is massive, you should probably have an extensive feature set. If your language is minimalist, it probably shouldn't depend on llvm
I don't think it's a strange critique point - dependencies are a necessary evil, but still an evil. Minimizing dependencies is always a good thing, and I can't think of a scenario where maximizing dependencies would be desirable, given the same amount of features.
It's a pretty weird critique, llvm is becoming ubiquitous for these tasks. Can rattle off probably a dozen programming languages using it and no one is grousing...
> dependencies are a necessary evil, but still an evil
I'm curious about this assertation. I've also heard that "do one thing and do it well" is a desirable goal and one should offload tasks to other pieces that specialize in that task (e.g. don't write your own crypto, use existing libraries), is there something that makes a programming language different?
Compile time/runtime dependencies often increase maintainence needs and resource usage.
In the context of programming languages, dependencies are a property of a programming language implementation. LLVM is not a small system, yet PicoLisp is a small language, which is why it might be considered a heavy dependency. There are scenarios like embedded where having a tiny implementation with little dependency pays off.
Ordinary programs can be very easily maintained by using only the language's standard library, as standard library is often maintained substantially longer than many third-party libraries.
PicoLisp does not depend on LLVM. The PicoLisp implementation pil21 uses LLVM in it's build process of the interpreter. There are multiple implementation of PicoLisp for different use cases. miniPicoLisp targets embedded. pil64 uses some meta assembly that can has an x86_64 and ARM64 port. pil32 is implemented in just C + Variable length arrays. Ersatz runs on the JVM.
It depends on the dependency. Linking sqlite.c doesn't bother me much as it's self contained and more likely to be correct than whatever I'm linking it into.
LLVM is pretty close to internally self contained as it DIY or vendors things in traditional C++ fashion (has optional deps on zlib and ncurses), especially if you use libc++ with it. However it is enormous and continually in flux. Building on LLVM also means picking up C++-like semantics regarding aliasing/undef/poison/freeze which are themselves somewhat in flux.
So in this case if PicoLisp emitted aarch64/x64 directly I'd consider that smaller/simpler than depending on LLVM, despite the increase in complexity in PicoLisp's own codebase from DIY'ing the machine code generation.
Fundamentally moving a load of complexity into a library can be a win but is not intrinsically cheaper than keeping it internal. You don't pay for it in gold - you move dev time and failure modes around, possibly for a net win and possibly for a net loss.
> If your dependency set is massive, you should probably have an extensive feature set.
Assuming that having a huge feature set is a desirable quality. Sometimes minimalism is a useful goal in itself - languages with few features usually make those features composable, leading to a combinatorial explosion of the ways those features can be used.
That being said, a small independent compiler/interpreter for PicoLisp would still be nice, though.
A minimal set of composable features is by far my preference for language design, hence reading about PicoLisp. I suppose a large and complicated set of dependencies could form the foundation for a minimal language - something like building a Forth on the JVM - but somehow that feels wrong. Misapplied priors in my reasoning perhaps.
The picolisp language doesn't differ between the custom assembly language implementation, the llvm implementation pil21 or the Java implementation Ersatz. Saying that picolisp has become less minimalistic because one implementation has a library attached which you think is heavy weight makes me think you misunderstood something important somewhere.
I was missing that there are implementations other than the llvm one, just went looking and found https://software-lab.de/doc64/README which I am delighted by, thank you for the hint!
Interesting enough the LLVM version is a new rewrite from 2021 (hence pil21).
There have been multiple rewrites but the last version I remember was written in pure generic 64 bit assembly without any dependency on C.
See: https://software-lab.de/doc64/README
Haven't yet found any rationale on the LLVM rewrite.
If you look into the code they do use LLVM exactly where they previously has their own generic assembly thing. It is probably to allow targeting more CPU platforms.
PicoLisp was the first programming language i choose to study because i wanted to learn more about programming language design. It really, really embodies "Code is data and data is code" while the built-in core are just C calling conventions pointers the interpreter jumps to everything above that is neatly packed AST in memory. The no-sql database is just neatly packed binary AST stored on disk and is accessed via pointers into the database files.
Nowadays i mostly do numerical work where an all interpreted language with only infinite precision integers (or fixed point numbers thinly wrapped on top) is not really suited but if i need to do a complicated automatic code generation pipeline, i just write a short picolisp script which prints the source code in the target language. The ability to execute and modify ASTs as the nested lists they are is just so convenient.
> picolisp script which prints the source code in the target language. The ability to execute and modify ASTs as the nested lists they are is just so convenient.
I'd be interested in seeing some of your code doing this. I've done lots of Scheme and some Clojure and CL, and in my own programming style I mostly avoid using bare s-expressions to represent ASTs (I use structures/objects instead[1]) except in macros, because that is giving me less ambiguity and better error checking. I wonder whether we are working at different levels of scale, or different ways of working that make us prefer one over the other.
This one is worth a closer look for scheme / common lisp devs. It's really not the same thing.
Dynamically scoped, first class environments, fexpr instead of macros. A corollary of that feature set is interpreted, not compiled.
What we have as a result is simpler than scheme. The function/macro distinction is gone and the module system is first class environments. I haven't got as far as working out whether they've done away with phase separation.
It's then tied into a database which seems pretty weird to me but that could totally be my limitation.
29 comments
[ 0.18 ms ] story [ 76.5 ms ] threadI wonder why never got many fans in spite of the strong tries from its author to promote it.
It's quite hard to get into that territory with different ideas, you'd have to provide a rather good value add for that. Even newLisp[1] didn't, and that both had various implementations and a standard. Clojure did, and not just because of the JVM (cf. ABCL/Kawa).
picoLisp just never really presented itself as being particularly unique for a niche. (IIRC also didn't run on Windows)
[1]: https://en.wikipedia.org/wiki/NewLISP
Common Lisp has most of the mindshare, followed closely by Clojure and the various Scheme implementations.
It is also unfortunate that picolisp can be difficult to install on macOS and Windows.
Seriously though, I think I like Common Lisp so much just because I have been using it since about 1982 and I am just very used to it. For a lot of work, I will just use Python if there are one or more Python libraries that solve whatever problem I am working on with just a few lines added Python code that I need to write.
I'm curious about this assertation. I've also heard that "do one thing and do it well" is a desirable goal and one should offload tasks to other pieces that specialize in that task (e.g. don't write your own crypto, use existing libraries), is there something that makes a programming language different?
In the context of programming languages, dependencies are a property of a programming language implementation. LLVM is not a small system, yet PicoLisp is a small language, which is why it might be considered a heavy dependency. There are scenarios like embedded where having a tiny implementation with little dependency pays off.
Ordinary programs can be very easily maintained by using only the language's standard library, as standard library is often maintained substantially longer than many third-party libraries.
LLVM is pretty close to internally self contained as it DIY or vendors things in traditional C++ fashion (has optional deps on zlib and ncurses), especially if you use libc++ with it. However it is enormous and continually in flux. Building on LLVM also means picking up C++-like semantics regarding aliasing/undef/poison/freeze which are themselves somewhat in flux.
So in this case if PicoLisp emitted aarch64/x64 directly I'd consider that smaller/simpler than depending on LLVM, despite the increase in complexity in PicoLisp's own codebase from DIY'ing the machine code generation.
Fundamentally moving a load of complexity into a library can be a win but is not intrinsically cheaper than keeping it internal. You don't pay for it in gold - you move dev time and failure modes around, possibly for a net win and possibly for a net loss.
Assuming that having a huge feature set is a desirable quality. Sometimes minimalism is a useful goal in itself - languages with few features usually make those features composable, leading to a combinatorial explosion of the ways those features can be used.
That being said, a small independent compiler/interpreter for PicoLisp would still be nice, though.
There have been multiple rewrites but the last version I remember was written in pure generic 64 bit assembly without any dependency on C. See: https://software-lab.de/doc64/README
Haven't yet found any rationale on the LLVM rewrite.
LLVM would only be a dev dependency for developing or compiling picolisp then, which is fine.
Nowadays i mostly do numerical work where an all interpreted language with only infinite precision integers (or fixed point numbers thinly wrapped on top) is not really suited but if i need to do a complicated automatic code generation pipeline, i just write a short picolisp script which prints the source code in the target language. The ability to execute and modify ASTs as the nested lists they are is just so convenient.
I'd be interested in seeing some of your code doing this. I've done lots of Scheme and some Clojure and CL, and in my own programming style I mostly avoid using bare s-expressions to represent ASTs (I use structures/objects instead[1]) except in macros, because that is giving me less ambiguity and better error checking. I wonder whether we are working at different levels of scale, or different ways of working that make us prefer one over the other.
[1] I guess it's clear what I mean--basically coding like in an object oriented language, albeit without mutation--but here's an example (definitions and use): https://github.com/pflanze/chj-schemelib/blob/master/coresch... https://github.com/pflanze/chj-schemelib/blob/master/coresch...
https://picolisp.com/wiki/?home
Dynamically scoped, first class environments, fexpr instead of macros. A corollary of that feature set is interpreted, not compiled.
What we have as a result is simpler than scheme. The function/macro distinction is gone and the module system is first class environments. I haven't got as far as working out whether they've done away with phase separation.
It's then tied into a database which seems pretty weird to me but that could totally be my limitation.