GregBuchholz 1 day ago

> no special syntax for anything.

    (list '(1. . #\#)
          -5/6+7.s-8i
          `(1 ,@2)
          1@1           ;hmmm, no unquote splicing comma ;-)
          10#           ;surprised?
          (list #i+1 +1i 1+i) ;complicated or complex?
          #e-1e10i     ;Old MacDonald?
          "(* 9 10)" #())
  • jqpabc123 1 day ago

    It looks more like a "program" for an HP15c pocket scientific calculator.

    Not necessarily a bad thing but also not exactly what I would call "expressive".

    You may be able to build anything you want --- which may be efficient when communicating with the machine. But I wonder about now well this would communicate with other programmers.

    • jibal 1 day ago

      It's not a program, it is (obviously) a totally unrealistic hodgepodge expression for a list of constant values that simply demonstrates the literal syntax for a variety of types of values (real numbers, dotted pairs, rational numbers, complex numbers, polar coordinates, etc. -- Racket is unusually expressive in the types of literals). Your comment demonstrates something typical of HN, and it's not a good thing. It's as if in a discussion about C someone posted, without explanation, an entry from the Obfuscated C contest and then someone else, making zero effort to understand what was posted, said that it looked like line noise and questioned whether C is conducive to communication between programmers.

      • euduhdhbene 12 hours ago

        First time dealing with developers I see. I’m sorry.

      • zelphirkalt 11 hours ago

        I think in this case it is probably justified, as the claim of "no special syntax" is unfortunately not true. To convey the amount of special syntax, it got crammed into one brief example snippet.

        Still, far less than most other programming languages, virtue of having parentheses making things unambiguous. Outliers are Smalltalk and maybe (?) Forth.

    • Joel_Mckay 12 hours ago

      I actually wrote an audio sequencer in HP RPL as a kid.

      It was far less complex to code, and human readable. Unicon was pretty cool also, but never became popular. Erlang/Elixir will probably become more relevant as people move into massively parallel constraint solvers.

      The monolith centric languages are simply no longer appropriate for many classes of problems. =3

      • jqpabc123 7 hours ago

        Unicon was pretty cool also, but never became popular.

        Like it or not, popularity is a significant attribute if you have to work with others.

  • nilslindemann 1 day ago

    Curious, how would look that in Python?

    • zelphirkalt 12 hours ago

      Would need some imports:

          import cmath
          from fractions import Fraction
      

      And then have to write numbers as strings to input them. From their examples:

          >>> Fraction('1.414213 \t\n')
          Fraction(1414213, 1000000)
      
          >>> cmath.sqrt(-2-0j)
          -1.4142135623730951j
      

      Not really great, but workable. I don't like having to import things for basic math stuff like exact numbers, but having a lot of special syntax is maybe also not that great. I am guessing except for maybe macros, there is no other way, if one wants to take input (code) literally as exact numbers in all cases. If one were to just write down a number like `1.414213562373`, and expect it to be exactly represented in the program, then it cannot be read as a floating point number first and then converted into an exact number internally, because reading it as floating point number could already lose precision. So this kind of logic to interpret exact numbers would have to live somewhere before that. So it is in the reader. I am not sure how one could employ a macro instead, and then maybe access the digits of a written number, or whether that is possible. I think it should be possible. If it is, then of course things could be made into macros, after which one could use something like:

          (exact 1.4142135623730951)
      

      [1]: https://docs.python.org/3/library/cmath.html [2]: https://docs.python.org/3/library/fractions.html

  • soegaard 1 day ago

    Yeah - the number syntax is pretty cool.

        #i is the prefix for inexact
        #e is the prefix for exact
    

    So `#e0.1` is exact 0.1 which means it will be read as an exact fraction 1/10 and `#i0.1` will be read as a floating point number.

    The `i` in `8i` is the imaginary unit. The `1@1` is the polar notation for complex numbers.

    The `10#` is a compatibility remain (the Scheme authors wanted a way to see how many signifant digits were known. In `10#` there 2. So `#` stands for "some digit". In most implementations this is read as 0. More details at [1]

    An `#` followed by a list datum is read as a vector. Thus `#()` is an empty vector and `()` is an empty list.

    [1] https://stackoverflow.com/a/10936403/23567

    Complicated? Well, to support both inexact (floating point) and exact numbers (bignums and fractions) it makes sense to have `#i` and `#e` to explicitl choose. The default is (more or less): numbers with . are inexact the others are exact.

  • Syntonicles 1 day ago

    At first I was frustrated by this comment, having a fair amount of Lisp experience failing to grok the examples. In less than one minute I was able to locate, download and install the latest Racket and was in a REPL. There really are no excuses.

    This was a wild ride. I'm torn on whether this feels like the Reader is bloated/polluted or whether this is the coolest numerical parser I've ever used. You can really do a lot to coerce the these numbers just by inserting an {o,s,e,i,b,#} into the number, with different semantics depending on the position of the character.

    Somewhat overwhelming, I can only hope there's some elegance underlying it all...

  • shawn_w 22 hours ago

    You forgot hash table and regular expression literals.

    • GregBuchholz 22 hours ago

      It looks like Racket nerfed the shared-structure / "Datum Labels" for circular lists and the like (I see it is still in section 2.4 of r7rs):

        '#0=(0 . #0#)
      

      ...and I should have included `#|block comments|#`, datum `#;comments`, and probably `|symbols with spaces|`.

        (let ((|1 + 2| (+ 1 2)))
          (display |1 + 2|))
      • soegaard 21 hours ago

        That has been in Racket for a long time.

        • GregBuchholz 21 hours ago

          I tried:

            #lang racket/base
            (let ((a '#0=(0 . #0#)))
              (display (car a)))
          

          ...over at

          https://onecompiler.com/racket

          ...and it didn't compile, complaining:

            read-syntax: `#...=` forms not  enabled for `read-syntax` mode
          

          ...maybe there is a switch needed to enable it?

          • soegaard 21 hours ago

            I am not 100% sure what's going on, but you can use `read` mode this way:

                #lang racket
                (let ((a (read (open-input-string "#0=(0 . #0#)"))))
                  (display (car a)))
            

            I think the problem is that `read` / `write` support shared data constructed using `shared`. But programs (read by `read-syntax` are not allow to have cycles.

          • shawn_w 21 hours ago

            Yeah, there's a toggle for it in read-syntax mode.

            https://docs.racket-lang.org/reference/Reading.html#%28def._...

            • GregBuchholz 20 hours ago

              How do you get that to work?

                #lang racket
                (read-syntax-accept-graph #t)
                (let ((a '#0=(0 . #0#)))
                  (display (car a)))
              

              ...which of course doesn't work, because `(read-syntax-accept-graph)` is a run-time thing, and the circular-list structure is a `read`-time thing.

              I couldn't figure it out with this either:

              https://stackoverflow.com/questions/51942188/read-syntax-for...

              ...just to make sure I wasn't going crazy, this:

                (let ((a '#0=(0 . #0#)))
                  (display (car a)))
              

              ...does work as expected with this online scheme interpreter:

              https://www.jdoodle.com/execute-scheme-online

              • shawn_w 17 hours ago

                It might take a custom #lang. I'll play around with it when I have some free time...

                That read-syntax doesn't just accept everything accepted by plain read is one of my biggest annoyances with Racket.

  • liendolucas 11 hours ago

    I think you clearly exposed one of the things that I couldn't never describe why I wasn't dragged into Racket/scheme. It's not only annoying is hard to remember all these differences.

    Obviously the more you use the language these go away but still at least to me it doesn't seem to be a coherent syntax.

  • fweimer 8 hours ago

    Isn't at least one of them undefined? `(1 ,@2)

    It's always surprising to me how many things Scheme leaves undefined. Other examples are evaluation order (in some rather surprising places) or the value of: (make-bytevector 4)

    • soegaard 7 hours ago
          > `(1 ,@2)
          '(1 . 2)
      

      This is a pair. The notations is called "dotted pair".

SA9G 23 hours ago

My first language, 1980 in CMU, was Pascal... quickly followed by LISP in CS/AI courses and C for EE courses. I was a big fan of MacLisp at the time, the MIT version that GLS worked on (could see his comments all over the sources). Of course GLS moved on to Scheme and wrote a few definitive papers on closures. Yeah, that was before PC's everywhere... a loooong time ago. (C++ came years and years afterwards, not a fan. I would have stayed with hardware if the ++ version of C was forced on me all those years ago.)

GLS moved to CMU in the early 80's... I remember taking "Comparative Programming Languages" from him. Good teacher and impressive guy. In addition to the obvious, he also covered SNOBOL and APL in that course. Memories :-)

perrygeo 1 day ago

> In The Amazing Digital Circus (episode 8, "hjsakldfhl"), when Kinger opens the terminal to try to reset Caine, you can see that Caine (a creative AI built in 1996) is programmed in Lisp. The file is literally named Caine-core.lisp.

Nice touch. Explains how Caine returns in episode 9: Lisp continuations allow for graceful error recovery.

  • valorzard 1 day ago

    wasn't Caine written in common lisp? Pretty sure they show that the debugger they used in the command line was gdb (which ... is that even actually possible?)

M0THXYZ 19 hours ago

Hiii, I'm Geometridae (Astrid Motilla)! A friend from the Racket community mentioned that my article appeared here. Thank you for reading it, and I'll make sure to take all this feedback and these different perspectives into account for this and future articles! :)

I’d also encourage you to give it a try and have some fun with it! I’m using it for some of the 3D demos in my book. It’s definitely not a silver bullet, but I’ve found it can be pretty productive.

Personally, my favorite Lisps are Racket, Common Lisp, and Clojure, in that order. I’ve also been tempted to give Chicken Scheme a try! :)

  • iainctduncan 15 hours ago

    If you like those, a less well knowned Scheme you will likely really like is s7. :-)

fn-mote 1 day ago

Much as I am a fan of Racket, this is not a friendly intro. It is a speedrun.

When an introduction says “friendly”, I don’t expect it to assume that I know what lambda is.

When an introduction says “friendly”, I don’t expect syntax rules to appear in it. At all.

  • allthetime 1 day ago

    It is rich with information, to the point, and presented in a way that a motivated and competent reader can immediately know something about and be useful with racket. That’s how it was for me anyways; someone who didn’t know anything about racket before reading through this.

    Wtf kind of intro to a programming language doesn’t include syntax rules? We are all informed, experienced, technologically inclined people here. Not dumb children.

    I’m struggling to see your comment as anything more than contrarian babble.

    • sudahtigabulan 1 day ago

      > Wtf kind of intro to a programming language doesn’t include syntax rules?

      PP could have been more clear and say syntax-rules (note the dash), which means macros. Macros are usually considered an advanced topic.

      • wormius 1 day ago

        Thanks for the clarification! It makes much more sense seeing the term "macro".

    • dented42 1 day ago

      > Wtf kind of intro to a programming language doesn’t include syntax rules?

      Given that most languages don’t have syntax rules or anything like it, I’m gonna go out on a limb and say that most language introductions/overviews/tutorials don’t mention it.

      • AnimalMuppet 1 day ago

        Most? I question that.

        In fact, what languages can you say do not have syntax rules?

        • aobdev 1 day ago

          Python and Java do not have any similar constructs

        • jibal 1 day ago

          syntax-rules refers specifically to Racket macros.

          Since macros are a very important part of Racket, complaining that a friendly introduction mentions them is [insert non-friendly characterization].

        • dented42 23 hours ago

          C, python, java, Fortran, ruby, Common Lisp, Haskell, go, zig (I think), forth, swift, cobol, pretty much any language that doesn’t directly descent from Scheme.

  • phlakaton 1 day ago

    When I see "friendly introduction," I don't expect to see a deadhead sticker front and center. But I guess if you're an actual Deadhead it's not that startling. ;-)

    • wormius 1 day ago

      TBF, deadheads are pretty friendly (just stay away from the Nitrous Mafia) LOL.

  • y1n0 1 day ago

    not to mention a skull? is it a racket thing (or lisp in general thing) to spoof the grateful dead logo?

M0THXYZ 19 hours ago

On a more personal note, for some weird reason, Racket ended up being the language that got me one of my most important contracts. Through a whole series of butterfly-effect events, that eventually led me into CAD software development, which is where I discovered my love for metamaterials. It’s funny how these things happen!

  • slim 12 hours ago

    because autocad is scripted in lisp ?

wild_egg 21 hours ago

> Emacs Lisp — millions of people run Lisp every day without knowing it, because their editor is a Lisp interpreter.

Are there seriously any emacs users who are completely unaware of elisp? Isn't elisp the entire point of picking emacs?

  • BeetleB 19 hours ago

    Most Emacs users I personally know simply treat it as an editor. They're not really aware how it works under the hood.

    • zelphirkalt 11 hours ago

      Still doubt, that they are unaware of Elisp. Bare bones vanilla Emacs is maybe a bit much to get used to these days, so anyone wanting to configure their Emacs will have opened their init.el or so file and will have put some Elisp snippets there.

pjmlp 15 hours ago

Interesting article overall, although some gotchas on the introduction.

Misses out on LispWorks and Allegro Common Lisp for the Common Lisp entry, which contrary to SBCL provide a similar graphics experience as the Lisp machines of yore, here missing out on TI and Xerox as well, Genera wasn't alone.

zerr 1 day ago

While the language is interesting, sadly, nobody is using it in the wild. Maybe due to cumbersome deployment options? The ability to produce native standalone executables would boost its usage I believe.

  • throwaway27448 1 day ago

    Racket has been able to produce standalone executables for a while now.

    • gus_massa 1 day ago

      I agree. In 2020 we had to manipulate some Moodle files. I wrote the code in Racket and I sent the ".exe" file to my coworkers, that never knew they were using Racket.

    • Vedor 1 day ago

      For years, really.

  • dented42 23 hours ago

    Given that a lot of people use it in the wild I’m not sure where you’re getting that from.

  • mark_l_watson 21 hours ago

    I wrote my own AI coding harness in Racket, it works fine, meets my own needs. I did earlier versions in Common Lisp and Python but the Racket version is the most full featured.

  • velcrovan 17 hours ago

    Not only do I produce standalone executables in Racket, I cross-compile GUI apps written on MacOS to produce executables for Windows.

  • zelphirkalt 11 hours ago

    It is not due to deployment options. People put all sorts of stuff in docker containers every day. It is rather that few people make an effort to learn a lisp, and fewer still who choose to learn Racket. The few universities teaching good computer programming basics using Racket don't really make a dent. You simply don't have coworkers who know this stuff. Are you going to be the one person making a decision at the company, to use something no one but you knows? Not gonna happen.

    I have not had a single coworker, who has even started to learn these things, even after I have presented a Scheme on a Friday afternoon, and showed off some cool threading/pipeline syntax-rules macro, and showed how this compared to Python, showing how it reduces boilerplate in a way impossible to do in Python.

    Granted, this is in Germany, where CS education is not really all that great, and most universities teach boring Java or C++ or something.

    At least we had a RacketCon in Berlin some time ago, and I was fortunate enough to have a prof at university, who at least introduced Dr.Scheme. His motto was: "I am not here to teach you C, Java, or Python. I am here to teach you computer programming." (translated) He went on to introduce languages of various paradigms, from Prolog, to Dr.Scheme (predecessor of Racket), to C, and Python. Thank you Prof. von Löwis.

  • hyperbolablabla 10 hours ago

    Naughty Dog uses Racket for all their scripting...

em-bee 1 day ago

any time the topic of racket comes up, i wonder if there are any interesting apps i could explore. but all i find is libraries and dev tools: https://awesome-racket.com/

  • mono442 1 day ago

    it's an academic language

    • spike021 1 day ago

      We used it in the course I had that introduced functional programming. The only memorable thing I can remember writing in it for that class was a topological sort (I believe).

    • so-cal-schemer 22 hours ago

      The co-creator of Scheme is also co-author of Structure and Interpretation of Computer Programs[0]. Namely, Gerald Jay Sussman[1], the Panasonic Professor of Electrical Engineering at the Massachusetts Institute of Technology (MIT).

      In teaching our material we use a dialect of the programming language Lisp. We never formally teach the language, because we don’t have to. We just use it, and students pick it up in a few days. This is one great advantage of Lisp-like languages: They have very few ways of forming compound expressions, and almost no syntactic structure. All of the formal properties can be covered in an hour, like the rules of chess. After a short time we forget about syntactic details of the language (because there are none) and get on with the real issues—figuring out what we want to compute, how we will decompose problems into manageable parts, and how we will work on the parts. Another advantage of Lisp is that it supports (but does not enforce) more of the large-scale strategies for modular decomposition of programs than any other language we know. We can make procedural and data abstractions, we can use higher-order functions to capture common patterns of usage, we can model local state using assignment and data mutation, we can link parts of a program with streams and delayed evaluation, and we can easily implement embedded languages. All of this is embedded in an interactive environment with excellent support for incremental program design, construction, testing, and debugging. We thank all the generations of Lisp wizards, starting with John McCarthy, who have fashioned a fine tool of unprecedented power and elegance.

      [0]: https://sarabander.github.io/sicp/

      [1]: https://en.wikipedia.org/wiki/Gerald_Jay_Sussman

  • eru 1 day ago

    You can ask Claude to write you some.

infinitebit 18 hours ago

The second recursion example introduces 2 syntax features without explaining them (let, square brackets)

vatsachak 1 day ago

I've never seen the appeal in schemes other than hot reloadability...

  • WalterGR 1 day ago

    Homoiconicity.

    • chowells 1 day ago

      What makes this a desirable feature? From the perspective where local reasoning is the most desirable property a language can have, how does homoiconicity support that?

      I honestly am curious. I've seen a few examples presented for it, but they always seem like bad software engineering to me. Where's an example that does something in a cleaner way than alternatives present in other languages while remaining compatible with local reasoning?

      • bjoli 1 day ago

        it allows you to create a language that compiles to your original language.

        You can abstract everything away. Not like Haskell where laziness accounts for some and typeclasses for some (and often an exponential growth in compile times). No, it property let's you change the language.

        I got tired of loops sucking and made this, for example: https://rikspucko.koketteriet.se/bjoli/goof-loop

        • chowells 1 day ago

          This doesn't strike me as better than the alternatives. In Haskell, it's just a few different functions to handle the different use cases. That strikes me as far better than one "function" that magically does different things depending on how it's called. It is local, I'll give you that. But it still seems bad because you have overloaded the semantics along multiple axes simultaneously. I prefer building blocks with only a single set of semantics each. I believe it's better to write precise code than DWIM code.

          • bjoli 12 hours ago

            everybody agrees, which is why most sane people very rarely reach for macros. At the same time, it would be great if some of the libraries that rely on typeclass machinery for code generation could use lisp-like macros, because turning a 3 second recompile to a 35 second recompile (and I have experienced worse. Much worse.) is what kills exploratory programming. I heard optics mostly solves much of the problems of generic lens, but god forbid you use something like servant or polysemy.

            Chez compiles a project of about 35000 lines (with heavy macro usage) in less than 0.5s on -O2.

      • WalterGR 1 day ago

        > From the perspective where local reasoning is the most desirable property a language can have

        That's a perspective. If you're looking for a low-level language, then Scheme isn't it. (Forget iconicity - Scheme is garbage-collected. And supports continuations!)

        If you don't program in machine code - which would maximize local reasoning - then you must know the language with the Correct balance of local reasoning and higher-level constructs. Knowing which language that is would add specificity to this discussion...

        • chowells 1 day ago

          That's not at all what local reasoning means. Local reasoning is the property that a piece of code contains (when including the call graph) everything that can affect what it does. All mutation of a value is kept within some scope of ownership of that value. If you want to understand a piece of code, you can do it by understanding that piece of code, not the program as a whole.

          Assembly makes non-local reasoning mandatory, as any code can update any location in memory without restriction. All memory accesses are global. References need not even be by name - they can be via computed addresses. There are no restrictions in place allowing the structure of the program to provide boundaries on what pieces of code may be understood as units.

          • so-cal-schemer 23 hours ago

            Homoiconicity is orthogonal to local reasoning. It just means the syntax is represented by the native data format, nested lists. This does make code generation very straightforward with list processing primitives.

            Mutation in scheme is possible, via set!, and set-car! and set-cdr!, but it's not recommended. Functional program design side-steps the issue.

            see also:

            SICP: 3.1.3 The Costs of Introducing Assignment

            https://sarabander.github.io/sicp/html/3_002e1.xhtml#g_t3_00...

            • chowells 21 hours ago

              Yes, I know they're orthogonal. But every example of using macros I see in lisp is either some horrendous non-local logic, or something that can be done better using simpler tools. Where's the killer use case that clearly is good software engineering and not papering over the lack of another feature?

              • so-cal-schemer 20 hours ago

                I believe this was built with macros:

                Coalton is an efficient, statically typed functional programming language that supercharges Common Lisp by taking great ideas from Haskell, Scheme, and OCaml.

                https://coalton-lang.github.io/

                Not well known, but surely good software engineering adding features. Lisp* is called the Programmable Programming Language for a reason.

                pg wrote a treatise on Lisp macros (free download):

                https://www.paulgraham.com/onlisp.html

                (add to that something about great power - great responsibility and not holding it wrong)

        • so-cal-schemer 23 hours ago

          https://prescheme.org/

          Pre-Scheme is a statically typed dialect of the Scheme programming language, combining the flexibility of Scheme with the efficiency and low-level machine access of C. The compiler uses type inference, partial evaluation, and other correctness-preserving transformations to compile a subset of Scheme into C with no additional runtime overhead. This makes Pre-Scheme a viable alternative to C for programming virtual machines, operating systems, and embedded systems where the runtime overhead of a complete Scheme implementation is not desirable.

          https://ryansuchocki.github.io/microscheme/

          Microscheme, or (ms) for short, is a functional programming language for the Arduino, and for Atmel 8-bit AVR microcontrollers in general. Microscheme is a subset of Scheme, in the sense that every valid (ms) program is also a valid Scheme program (with the exception of Arduino hardware-specific primitives). The (ms) compiler performs function inlining, and features an aggressive tree-shaker, eliminating unused top-level definitions. Microscheme has a robust FFI (Foreign Function Interface) meaning that C code may be invoked directly from (ms) programs. Therefore, the power of the existing wealth of Arduino libraries is available within Microscheme.

          • GregBuchholz 23 hours ago

            Also, CRUNCH from Chicken Scheme:

            https://wiki.call-cc.org/eggref/6/crunch

            • so-cal-schemer 22 hours ago

              Ah, thank you. It slipped my mind.

              CRUNCH is an embedded compiler for a statically typed subset of R7RS Scheme, generating C code. The compiler uses type inference to decorate the code with type information without requiring declarations. CRUNCH can be used to translate embedded Scheme code sections, whole programs or multiple source modules into standalone executables or compiled code that can be invoked from Scheme.

              The generated C code uses a small runtime-system contained completely in a single C header file. Reference counting is used for managing aggregate data like strings which removes the need for full tracing garbage collection or manual memory management while still having a relatively small overhead.

              Since more or less a direct translation of Scheme to C is done, the generated code should run at roughly the same performance as C. No type-checking takes place as the types of all values have been inferred at compile time, and values are not tagged. With the exception of reference counted objects there is no additional runtime overhead and Scheme and C can directly interchange data. This makes CRUNCH very appropriate for writing programs that need a maximum of speed or that are target for constrained environments like deeply embedded systems. The code is portable to all systems that at least have a C compiler.

              UNICODE strings are supported and can optionally be disabled for improving performance and reducing code size.

              CRUNCH is heavily inspired by PreScheme, the low-level compiler that is originally part of the Scheme48 project. In fact, CRUNCH can be considered a modern reimplementation of PreScheme written in and for use with CHICKEN.</i>

              See also:

                Crunch – a Scheme compiler with a minimal runtime (more-magic.net)
               190 points by sjamaan on Dec 17, 2024 | hide | past | favorite | 72 comments
              

              https://news.ycombinator.com/item?id=42440767

      • gus_massa 1 day ago

        Homoiconicity makes writing macros easy.

        If you don't like it, you can try https://rhombus-lang.org/ that is build on Racket and also has macros but uses a Python-like syntax.

        • Zambyte 23 hours ago

          Easy is an understatement. Macros and dynamic programming (often done using runtime reflection in other languages) are so trivially easy in Lisps that one can stumble into it without realizing they're even doing it. Anyone who has done these things in more "modern" languages knows these features are not something one can accidentally start doing. They take a lot of deliberate effort relative to standard, static programming.

          • soegaard 23 hours ago

            > Easy is an understatement. Macros and dynamic programming (often done using runtime reflection in other languages) are so trivially easy in Lisps that one can stumble into it without realizing they're even doing it.

            Well, getting the interaction between modules and syntax transformations (macros) right is not an easy task.

            "Composable and Compilable Macros: You Want it When?" Matthew Flatt http://dl.acm.org/authorize?24908

    • layer8 1 day ago

      I don’t find it appealing when everything looks the same irrespective of purpose and context.

      • eru 1 day ago

        Seems like a non-sequitur.

        • layer8 1 day ago

          How so? Homoiconicity means that everything uses the same form of representation, and I’m familiar with how these things look in Lisp.

          • eru 20 hours ago

            Homoiconicity doesn't mean everything has to _look_ the same.

            If the term has any meaning, it's about how program source code can be represented and manipulated easily by the user. (And I say 'can' deliberately, because even Lisps still allow you to treat source code as a big flat string, if you want to.)

            I don't think homoiconicity is all that much of a useful concept. It's very hard to pin down. Eg C can represent its own source code, too, if a bit clunkily. And Lisps generally don't execute by walking over s-expressions: their internal representation of their own logic typically uses more sophisticated structures (and adding native code compilers to the mix complicates matters further).

      • jibal 1 day ago

        Does i + 1 look the same as "i + 1"? The first is an expression, the second is a string. In LISP, it's similarly (+ i 1) vs '(+ i 1).

        These sorts of shallow complaints can be made of any feature than one is not familiar with, hasn't used, and doesn't know or understand the benefits of. In any case, no one is forcing people to use this language or take advantage of this feature.

        P.S. The "response" actually ignores all points made, attacks strawmen, moves the goalposts, and is intellectually dishonest (the original comment was clearly a complaint, and besides it wouldn't matter if some other word like "criticism" or "dissatisfaction" were substituted--my point remains). Again, no one is forcing anyone.

        • layer8 23 hours ago

          To answer your question, yes it does look pretty much the same in the Lisp version, less so in the string version. Also I didn’t complain; I expressed the opinion that I don’t agree that homoiconicity increases the appeal of a programming language, because the drawbacks outweigh the possible benefits for me. I spent some time programming in Lisp and am familiar with how homoiconicity works there.

          The point is that these things are subjective, and there will never be a programming language that everyone likes the best.

      • BoingBoomTschak 22 hours ago

        You can read the words, you know? Together with proper indentation that almost makes it look like Python, there's no way you can get lost in Lisp.

    • eru 1 day ago

      C has homoiconicity: you can represent C source code as C strings.

      • jibal 1 day ago

        You can't run that code within the language so no, obviously not. And even if you could, strings + eval alone is not homoiconicity--programs are not manipulated by the compiler as unstructured text strings.

        • eru 19 hours ago

          > You can't run that code within the language so no, obviously not.

          Of course, you can. C is perfectly capable of writing C interpreters and compilers.

          > And even if you could, strings + eval alone is not homoiconicity--programs are not manipulated by the compiler as unstructured text strings.

          Lisps (typically) don't execute by walking over s-expressions, either. Lisp interpreters and compilers use more sophisticated representations.

        • jibal 18 hours ago

          I quite intentionally said "within the language" to preempt nonsense like "Of course, you can. C is perfectly capable of writing C interpreters and compilers."

          • eru 17 hours ago

            What does 'within the language' mean?

            Is the standard library part of the language? Would a variant of C that came with an interpreter in the standard library (but no other changes) count as homoiconic?

      • Zambyte 23 hours ago

        You can't natively index into a string and get any C syntactic token out of it besides a char.

        • eru 19 hours ago

          You can write some library functions to get you more sophisticated indexing, if that's what you want.

          • Zambyte 15 hours ago

            The fact that you can write a C parser in C does not make C homoiconic. Not needing to have a user level parser is like, the whole thing that makes homoiconicity interesting.

      • so-cal-schemer 22 hours ago

        You can show this is possible, but it is extremely onerous in comparison.

  • so-cal-schemer 1 day ago

    Lisp/Scheme/Racket is a very simple language that is also very fluid and malleable. You can express ideas and computations very precisely, concisely, and intuitively. There really aren't any barriers as they (Scheme/Racket at least) are rooted in the Lambda Calculus which is a theoretical model equivalent to the Turing Machine. Any language feature you'd like can be expressed: Want standard infix math expressions or a borrow-checker? That's a macro. Want lazy evaluation? That's a simple change in a meta-circular evaluator. Want a language that is the simplest and most direct mapping to your problem domain? A DSL.

    The syntax lends itself to easy structural editing way before LSPs..

    Why Racket? Why Lisp?

    https://beautifulracket.com/appendix/why-racket-why-lisp.htm...

    Why language-oriented programming? Why Racket?

    https://beautifulracket.com/appendix/why-lop-why-racket.html

    Creating Languages in Racket: Sometimes you just have to make a better mousetrap.

    https://queue.acm.org/detail.cfm?id=2068896

    Lisp is clay: the power of composable DSLs

    https://fosdem.org/2026/schedule/event/HDE7JZ-lisp-is-clay/

    The other powers of Lisps (interactive development, hot reloadability, restarts on error, etc) are more tied to the sophistication of their implementations and runtime environments.

    • vatsachak 16 hours ago

      But I can use custom DSLs using macros in Rust or template Haskell. In Lean 4 you can even add grammar to the Lean parser.

      I feel like the good parts of lisps have been adopted by typed languages...

      • so-cal-schemer 15 hours ago

        Absolutely. You can thank Lisp for paving the way. And in a way, those languages are part of the extended family.

ux266478 1 day ago

Nothing against Lisp, but to correct the record:

> For decades, Lisp was the language of artificial intelligence. [...] Then came the "AI winter," funding dried up, and Lisp went from star to cult language.

Lisp had fallen from relevance before then. Only the United States was still using it, and mostly out of technical debt and a stubborn refusal to move on. Prolog displaced it in the late 1970s, and even within the US, the Lisp part was an unfortunate implementation detail to get to a Prolog-shaped object. It's not surprising that the US fell way behind Japan in this area in the 1980s. Anybody who would imply they would take a Lisp machine over a PIM is either not interested in symbolic computation and just likes Lisp, or they're doing so out of total ignorance of how much more advanced the PIMs were for symbolic AI.

The cutting edge was with Prolog, and that's still the case today. Nobody's researching MIL in Lisp, even in America.

  • BoingBoomTschak 22 hours ago

    And then mini/microKanren put Prolog in its rightful place: that of a useful DSL instead of a poor general purpose language.

    • ux266478 18 hours ago

      miniKanren is a replacement for a Prolog like a shitty tree-walking sexpr interpreter that doesn't even have modules is a replacement for Racket. I don't even know what to say to you for suggesting that, frankly.

      Anyways the embedded inference engine as an idea failed a long time ago, Americans convinced themselves it was the way to do things and just refused to ever let it go. It's just extra baggage on the important bit, which is the symbolic computation. miniKanren has a niche more in line with a theorem solver.

      • BoingBoomTschak 12 hours ago

        Instead of us two milling about: https://minikanren.org/minikanren-and-prolog.html (and this is both informed and pretty impartial from what I can read)

        • YeGoblynQueenne 6 hours ago

          I don't see where the above says that Prolog is a "poor general purpose language"? I'd instead say that claim is incompatible with the following paragraphs that rather sing the praises of Prolog as a programming language:

          Prolog is one of the two classic languages for symbolic artificial intelligence programming (the other classic language being Lisp). Prolog excels at implementing symbolic rule-based systems in which declarative knowledge is encoded in first-order logic. The language is optimized for expressiveness and efficiency for these types of applications, sometimes at the expense of logical purity. For example, by default Prolog does not use the "occur check" in unification. From a math/logic standpoint, this version of unification is incorrect. However, the occur check is expensive, and in most cases the lack of the occur check is not a problem. This is a very pragmatic design decision, as is Prolog's use of depth-first search, and use of cut (!) to control backtracking. I'm sure these decisions were absolutely necessary when running on the hardware of the 1970s, and today are very useful when working on large problems, and when dealing with huge (often infinite!) search spaces.

          Prolog supports many "extra-logical" or "non-logical" features, including cut, assert and retract, projection of variables for arithmetic using is, and so forth. Many of these features make it easier to express complex control flow, and to manipulate Prolog's global database of facts. One very interesting feature of Prolog is that Prolog code is itself stored in the global database of facts, and can be queried against at run time. This makes it trivial to write meta-interpreters that modify the behavior of Prolog code under interpretation. For example, it is possible to encode breadth-first search in Prolog using a meta-interpreter that changes the search order. This is an extremely powerful technique that is not well known outside of the Prolog world. 'The Art of Prolog' describes this technique in detail.

          Tremendous effort has gone into improving Prolog implementations, most of which are based on the Warren Abstract Machine (WAM). The WAM uses a side-effecting model in which values are destructively assigned to logic variables, with these side-effects being undone upon backtracking. Many features can be added to Prolog by extending the instructions of the WAM. One disadvantage of this approach is that Prolog implementation papers can be difficult to read without a solid understanding of the WAM. On the other hand, Prolog implementer have a common model for discussing implementation issues. There has been a great deal of research in parallel Prolog, culminating in Andorra Prolog in the 1990s. At least some of these ideas live on in Ciao Prolog. (Ciao Prolog is full of interesting ideas, many of which go far beyond the Prolog standard.)

          Prolog has a beautiful unification-based "pattern-matching"-style syntax that results in very succinct programs. Prologers love their syntax, just like Lispers love their s-expressions. Prolog also has a large library of standard predicates. Due to all of the engineering that has gone into making the WAM fast, there are very capable and mature Prolog implementations. As a result, many large knowledge-based systems have been written entirely in Prolog.

          So, why do you say that Prolog is a "poor general purpose language"?

  • so-cal-schemer 21 hours ago

    I've read a large factor was minicomputers and microcomputers running Unix in C became much more affordable, and when the Berlin Wall fell in 1989 DoD funding for powerful and expensive Lisp workstations quickly dried up. And the nascent free software Lisp offerings left a lot to be desired (to be developed).

    This visualization of relative programming language popularity shows Lisp was the 3rd most popular general purpose language in Q1 of 1984 behind Pascal (a learning language?) and C.

    (Prolog shows at 12th, then drops off the chart)

    Most Popular Programming Languages: Data from 1958 to 2025

    https://youtu.be/ZTPrbAKmcdo?t=116

    Gabriel's perspective from 1990:

    Lisp: Good News, Bad News, How to Win Big - Richard P. Gabriel - Lucid, Inc

    https://dreamsongs.com/WIB.html

    • ux266478 19 hours ago

      That's why nobody uses Lisp machines today, but Lisp is somewhat orthogonal. It's true that the death of the Lisp machine economy hurt Lisp more than the end of the FGCS hurt Prolog. BUT this was happening long before the winter hit.

      > This visualization of relative programming language popularity shows Lisp was the 3rd most popular general purpose language

      Which is fine, and makes sense. But I'm addressing a claim about Lisp being "the Language" for the era of symbolic AI. Which is decidedly not high volume, nor is it general at all, it's really quite specific.

      I'm correcting it, because I know Americans are heavily culturally silo'd on this, and symbolic AI is especially esoteric. Most people probably can't name a single commercial expert system off the top of their head, and that's not unreasonable. Our cultural view of symbolic AI is rife with American exceptionalism (I theorize partially because of government involvement) that just doesn't track with reality. Speaking as an American.

    • pasc1878 15 hours ago

      Pascal in 1984 was not just a teaching language.

      The ranking then was due to Turbo Pascal which came out in November 1983. It was probaly the first IDE and compiler on PCs

srcreigh 18 hours ago

Side note, does anybody know of some good Rhombus codebases I can read?

  • zelphirkalt 9 hours ago

    Fairly new, I think there will not be much Rhombus around yet.

ventana 22 hours ago

As the article correctly mentions, Lisp as a concept is much older than any of the popular languages used these days; so are the attempts to get more people use it. Most of them are, unfortunately, unsuccessful, because it's probably impossible to understand Lisp after a quick and friendly introduction.

I first met Common Lisp during my college years in early 2000s: we had an enthusiast teacher who taught it for free, in his free time; it was interesting for me as a freshman to learn something new, but also I literally had no idea how to write Lisp code properly, without resorting to using `progn` here and there. For those not aware, `progn` is a form that allows you to write expressions in a sequential way, pretty much like you use the C compound statement, a `{ ... }` block. It works, and after some time you find that you're writing in some crappy imperative programming language with a lot of brackets but you don't learn much about the functional programming.

It took me more than a year, and definitely more than any short “friendly introduction”, to understand the functional approach.

Now, Racket. I first noticed it while doing my daily LeetCode; they added Racket as a supported language and you can go use it to solve a task. After not using Lisp for 20-something years, I appreciated the opportunity, but I found the type contract syntax infuriating. This is LeetCode #1, “Two sum”:

  (define/contract (two-sum nums target)
    (-> (listof exact-integer?) exact-integer? (listof exact-integer?))
    )

I don't know what you feel, but this makes me want to close this tab and never return. So if I find myself wanting to write some Lisp and there's a daily LeetCode task I want to solve, the first thing I do is I delete all this garbage and come back to the clean option:

  (define (two-sum nums target) ...)

which, except for the minor syntax differences, brings me back to my college Lisp years and I can actually enjoy it.

SilentM68 23 hours ago

Thanks for sharing. It's been bookmarked :)

  .-.
 / λ \
 '---'
usxr1515 1 day ago

I've been designing my own small language runtime in Rust (VM + JIT + AOT backends) mostly as a way to actually understand tradeoffs compiler authors make instead of just reading about them. Racket's approach to macros and language-oriented programming is one of the things I keep coming back to as a reference curious how much of that flexibility comes at a real runtime cost vs. being mostly a compile-time abstraction.

  • brabel 22 hours ago

    I can answer that for Common Lisp, specifically the SBCL implementation (there's several others). It compiles every function to native code (you can even inspect the assembly with `disassemble`). Macros are executed before code is compiled. Hence, once you've compiled a function, the macro disappears since its only role is to generate the expressions that are going to actually be compiled and then executed.

    For example, here's a simple CL macro:

        (defmacro defer (cleanup &body action)
             `(unwind-protect (progn ,@action) ,cleanup))
    

    `unwind-protect` is like a `try/finally` in other languages, and I used that above to create something similar to `defer` in Go/Zig which is related to it, but with the operands inverted.

    The ` symbol is a quasiquote. Unlike quote `'` it lets you unquote symbols inside with the , operator. That's why you'll always see a bunch of '`' and ',' in macros.

    The @, thing is a "spread" (looks different in Racket from what I saw in the post, which used `...`). It just spreads whatever was on the list in the place you put that on, so if `action` is `(p 1) (p 2)`, then `(progn ,@action)` becomes `(progn (p 1) (p 2))`.

    You can inspect what the actual code that will be compiled looks like with `macroexpand`:

        CL-USER> (macroexpand '(defer (cleanup) (do-something)))
        (UNWIND-PROTECT (PROGN (DO-SOMETHING)) (CLEANUP))
    

    `progn` is a "special operator" that's needed when you want more than one expression to be evaluated in order.

    Example calling the macro:

        CL-USER> (defer (print "done") 
                   (print "hello")
                   (print "again"))
    
        "hello" 
        "again" 
        "done"
    

    As you can see, it executed the deferred expression last.

    We can prove that macros disappear after compile-time with an example:

        CL-USER> (disassemble (lambda (x) (+ x x)))
        ; disassembly for (LAMBDA (X))
        ; Size: 36 bytes. Origin: #x8005F70664                        ; (LAMBDA (X))
        ; 64:       AA0A40F9         LDR R0, [THREAD, #16]            ; binding-stack-pointer
        ; 68:       AA0B00F9         STR R0, [CFP, #16]
        ; 6C:       EA030CAA         MOV R0, R2
        ; 70:       EB030CAA         MOV R1, R2
        ; 74:       297E80D2         MOVZ TMP, #1009
        ; 78:       5E6B69F8         LDR LR, [NULL, TMP]              ; SB-KERNEL:TWO-ARG-+
        ; 7C:       DE130091         ADD LR, LR, #4
        ; 80:       C0031FD6         BR LR
        ; 84:       E00120D4         BRK #15                          ; Invalid argument count trap
    
    

    You can see the Assembly is very simple for `(+ x x)` (the ADD instruction plus a bunch of stack/error maintenance).

    If we instead had a macro that did this:

        (defmacro my-macro (x) `(+ ,x ,x))
    

    And a function used that:

        (defun f (x) (my-macro x))
    

    Now, disassembling the function:

        CL-USER> (disassemble #'f)
        ; disassembly for F
        ; Size: 36 bytes. Origin: #x8005C00374                        ; F
        ; 74:       AA0A40F9         LDR R0, [THREAD, #16]            ; binding-stack-pointer
        ; 78:       AA0B00F9         STR R0, [CFP, #16]
        ; 7C:       EA030CAA         MOV R0, R2
        ; 80:       EB030CAA         MOV R1, R2
        ; 84:       297E80D2         MOVZ TMP, #1009
        ; 88:       5E6B69F8         LDR LR, [NULL, TMP]              ; SB-KERNEL:TWO-ARG-+
        ; 8C:       DE130091         ADD LR, LR, #4
        ; 90:       C0031FD6         BR LR
        ; 94:       E00120D4         BRK #15                          ; Invalid argument count trap
    

    Same thing exactly.

    I don't know Racket, but knowing it can compile to binary, I expect macros in Racket would work exactly the same.

    • gus_massa 19 hours ago

      Yes, Racket is quite similar, perhaps with more steps (but I guess you also simplified the steps in SBCL). It's something like 20 steps, but let's keep it short:

      Racket ---expand the macros---> Simplified Racket "Kernel"

      Simplified Racket "Kernel"---schemify---> Rumble (that is Chez Scheme + a few libraries and macros)

      Rumble ---expand the macros---> Simplified Chez Scheme

      Simplified Chez Scheme ---constants propagation/folding/other---> Simplified Chez Scheme

      Simplified Chez Scheme ---compiler---> native code

      You can see the result with https://pkgs.racket-lang.org/package/disassemble