I love and relate to any impassioned plea on SWE esoterica, so this seems like as good of a place as any to ask: What, in practice, is this deep level of "homoiconic" or "symbolic" support used for that Python's functools (https://docs.python.org/3/library/functools.html) doesn't do well? As someone building a completely LISPless symbolic AGI (sacrilege, I know), I've always struggled with this and would love any pointers the experts here have. Is it something to do with Monads? I never did understand Monads...
To make this comment more actionable, my understanding of Python's homoiconic functionality comes down to these methods, more-or-less:
1. Functions that apply other functions to iterables, e.g. filter(), map(), and reduce(). AKA the bread-n-butter of modern day JavaScript.
2. Functions that wrap a group of functions and routes calls accordingly, e.g. @singledispatch.
3. Functions that provide more general control flow or performance conveniences for other functions, e.g. @cache and and partial().
3. Functions that arbitrarily wrap other functions, namely wraps().
Certainly not every language has all these defined in a standard library, but none of them seem that challenging to implement by hand when necessary -- in other words, they basically come down to conviences for calling functions in weird ways. Certainly none of these live up to the glorious descriptions of homoiconic languages in essays like this one, where "self-introspection" is treated as a first class concern.
What would a programmer in 2024 get from LISP that isn't implemented above?
The key for me really is in the signature for "eval." In python, as an example, eval takes in a string. So, to work with the expression, it has to fully parse it with all of the danger that takes in. For lisp, eval takes in a form. Still dangerous to evaluate random code, mind. But you can walk the code without evaluating it.
HackerNews sadly never fails to disappoint. Thanks for taking the time to share, that was exactly what I was looking for! Would endorse this link for any lurkers.
The LISP (elisp?) syntax itself gives me a headache to parse so I think I'll stay away for now, but I'll definitely be thinking about how to build similar functionality into my high level application code -- self modification is naturally a big part of any decent AGI project. At the risk of speaking the obvious, the last sentence was what drove it home for me:
It is not just some opaque string that gets to enjoy all of the benefits of your language. It is a first class list of elements that you can inspect and have fun with.
I'm already working with LLM-centric "grammars" representing sets of standpoint-specific functions ("pipelines"), but so far I've only been thinking about how to construct, modify, and employ them. Intelligently composing them feels like quite an interesting rabbit hole... Especially since they mostly consist of prose in minimally-symbolic wrappers, which are probably a lot easier for an engineer to mentally model--human or otherwise. Reminds me of the words of wonderful diehard LISP-a-holic Marvin Minsky:
The future work of mind design will not be much like what we do today. ...what we know as programming will change its character entirely-to an activity that I envision to be more like sculpturing.
To program today, we must describe things very carefully because nowhere is there any margin for error. But once we have modules that know how to learn, we won’t have to specify nearly so much-and we’ll program on a grander scale, relying on learning to fill in details.
In other words: What if the problem with Lisp this whole time really was the parentheses? ;)
Glad you liked the post. I didn't do any effort to make the elisp readable, so please don't let that fully put you off the topic! :D
I keep meaning to expand on the idea. I keep not doing so. I have higher hopes that I can get back to the rubik's cube code. Even there, I have a hard time getting going.
> In other words: What if the problem with Lisp this whole time really was the parentheses? ;)
I am yet to find a syntax style more ergonomic than s-expressions. Once you appreciate the power of structural code editing your view of s-expressions is likely to change
But they said "sadly". They said "HackerNews sadly never fails to disappoint".
If they really meant the opposite that "HackerNews never disappoints" why would that be "sad"?
But since they said "sadly" I think they really meant what they wrote which is that HN consistently disappoints them. Maybe they meant that the Lisp articles on HN do not go into describing exactly how "code as data" works in actual practical matters and that is consistently disappointing? And they are finally happy when someone explained "code as data" to them in the comments section?
The syntax of Lisp is made up of the same fundamental data types as you use when writing Lisp programs. `(+ 1 2 3)` is both a Lisp expression that evaluates to 6 and also a list containing four items, the symbol `+` and the numbers 1, 2, and 3.
In general, we can say that the Lisp language is very good at manipulating the same data types that the syntax of Lisp programs is made from. This makes it very easy to write Lisp programs that swallow up Lisp programs as raw syntax, analyze Lisp programs syntactically, and/or spit out new Lisp programs as raw syntax.
To make this comment more actionable, my understanding of Python's homoiconic functionality comes down to these methods, more-or-less:
1. Functions that apply other functions to iterables, e.g. filter(), map(), and reduce(). AKA the bread-n-butter of modern day JavaScript.
2. Functions that wrap a group of functions and routes calls accordingly, e.g. @singledispatch.
3. Functions that provide more general control flow or performance conveniences for other functions, e.g. @cache and and partial().
3. Functions that arbitrarily wrap other functions, namely wraps().
Certainly not every language has all these defined in a standard library, but none of them seem that challenging to implement by hand when necessary -- in other words, they basically come down to conviences for calling functions in weird ways. Certainly none of these live up to the glorious descriptions of homoiconic languages in essays like this one, where "self-introspection" is treated as a first class concern.
What would a programmer in 2024 get from LISP that isn't implemented above?