NHacker Next
  • new
  • past
  • show
  • ask
  • show
  • jobs
  • submit
A Friendly Introduction to Racket (geometridae.bearblog.dev)
GregBuchholz 1 days 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)" #())
soegaard 1 days 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 days 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...

fweimer 9 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 9 hours ago [-]

    > `(1 ,@2)
    '(1 . 2)
This is a pair. The notations is called "dotted pair".
GregBuchholz 7 hours ago [-]
Looks like fweimer is correct for scheme. I admit to being very surprised. Here is the verbiage from various rXrs:

r3rs, r4rs & r5rs (section 4.2.6):

https://standards.scheme.org/official/r3rs.pdf

https://standards.scheme.org/official/r4rs.pdf

https://conservatory.scheme.org/schemers/Documents/Standards...

"If a comma appears followed immediately by an at- sign (@), then the following expression must evaluate to a list;"

r6rs (section 11.17):

https://standards.scheme.org/official/r6rs.pdf

"If an (unquote-splicing 〈expression〉 . . . ) form appears inside a〈qq template〉, then the〈expression〉s must evaluate to lists;"

r7rs (4.2.8):

https://standards.scheme.org/official/r7rs.pdf

"If a comma appears followed without intervening whitespace by a commercial at-sign (@), then it is an error if the following expression does not evaluate to a list;"

soegaard 7 hours ago [-]
You need to use the Racket documentation.
fweimer 7 hours ago [-]
But 2 isn't a list, and the argument to unqoute-splicing must be a list:

> If an (unquote-splicing <expression> ...) form appears inside a <qq template>, then the <expression>s must evaluate to lists; the opening and closing parentheses of the lists are then “stripped away” and the elements of the lists are inserted in place of the unquote-splicing form.

nilslindemann 1 days ago [-]
Curious, how would look that in Python?
zelphirkalt 13 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
nilslindemann 18 hours ago [-]
that look*
jqpabc123 1 days 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.

Joel_Mckay 13 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 9 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.

Joel_Mckay 1 hours ago [-]
>popularity is a significant attribute if you have to work with others

Not sure why people buried your comment, but there is a good argument for long term support.

NodeJS is probably the worst to maintain if you cater to the lowest common denominator. Easy to show quick results with a low skill team, but generally anyone that can't be bothered to use other languages is going to cause reliability problems sooner or later. Ecosystems like pip/Cargo/Go-flavor-of-the-year/npm that run out-of-band dependency management will have deferred perpetual IT costs. =3

jibal 1 days 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.
zelphirkalt 13 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.

jibal 5 hours ago [-]
> I think in this case it is probably justified

What is "it"? The GP clearly doesn't understand that code and utterly misconstrues its nature and the nature of the Racket/Scheme/LISP language. If someone has zero knowledge of the topic at hand (even when it's a seminal programming language that's been around for nearly 70 years), they have an obligation to at least glance at TFA before commenting.

> the claim of "no special syntax" is unfortunately not true

That's a completely different issue than the one I addressed. (And I really don't think the point is made in good faith--the sense in which that phrase was meant is well understood, certainly by the GGP who is so very familiar with Racket's token syntax and quasiquotes.)

I won't respond further.

euduhdhbene 14 hours ago [-]
First time dealing with developers I see. I’m sorry.
jibal 5 hours ago [-]
Nonsensical non sequitur ad hominem. (FWIW I've been a software developer for 60 years and have dealt with many of them.)

I won't respond further.

shawn_w 23 hours ago [-]
You forgot hash table and regular expression literals.
GregBuchholz 23 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 23 hours ago [-]
That has been in Racket for a long time.
GregBuchholz 23 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 22 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 22 hours ago [-]
Yeah, there's a toggle for it in read-syntax mode.

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

GregBuchholz 22 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 13 hours ago [-]
It's ugly and probably fragile and prone to break, but I found a way. Added it as an answer on that Stack Overflow question: https://stackoverflow.com/a/79997503/9952196
shawn_w 18 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 13 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.

GregBuchholz 7 hours ago [-]
Well, to be fair, many/most programs aren't using complex numbers, or concerned with inexact vs. exact numbers, significant digits, or really even scientific notation.
BoingBoomTschak 14 hours ago [-]
How did it manage the feat of being more baroque than CL? Why that #e madness instead of https://www.lispworks.com/documentation/HyperSpec/Body/f_rat... ?
SA9G 1 days 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 days 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 days 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?)
perrygeo 1 days ago [-]
Yep, `/usr/bin/gdb /usr/local/bin/clisp 1337` ... also in TADC lore, windows has forward slashes.
GregBuchholz 1 days ago [-]
Windows has allowed forward slashes for directory separators for a long time. Seems like maybe it started with DOS 2.0.

https://en.wikipedia.org/wiki/Backslash#Filenames

M0THXYZ 21 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 17 hours ago [-]
If you like those, a less well knowned Scheme you will likely really like is s7. :-)
fn-mote 1 days 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 days 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 days 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 days ago [-]
Thanks for the clarification! It makes much more sense seeing the term "macro".
dented42 1 days 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 days ago [-]
Most? I question that.

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

dented42 1 days 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.
aobdev 1 days ago [-]
Python and Java do not have any similar constructs
jibal 1 days 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].

vgbm 7 hours ago [-]
Agreed. As someone who hasn’t used Racket it wasn’t clear to me why syntax rules needed to be used over a simple define or what syntax rules even did that define didn’t.
phlakaton 1 days 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 days ago [-]
TBF, deadheads are pretty friendly (just stay away from the Nitrous Mafia) LOL.
y1n0 1 days ago [-]
not to mention a skull? is it a racket thing (or lisp in general thing) to spoof the grateful dead logo?
dented42 1 days ago [-]
It used to be the official logo. The project was called PLT Scheme at the time.
soegaard 1 days ago [-]
The evolution of the logo:

https://users.cs.northwestern.edu/~robby/logos/

The skull didn't last long.

M0THXYZ 21 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 14 hours ago [-]
because autocad is scripted in lisp ?
M0THXYZ 7 hours ago [-]
Never touched AutoCAD, I know they have autoLISP , but yeah maybe
soegaard 1 days ago [-]
Btw - if people are interested in Racket related blogs / articles etc.

https://racket-stories.com/

wild_egg 22 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 21 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 13 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.
BeetleB 7 hours ago [-]
I've helped such people with their configs. They just know it as "commands" they put in the init file - they don't think of it as a programming language. It's rare for simple init.el files to have things like functions, loops, etc.

And, to be frank, most of them don't customize the init.el file. They just want a text editor - not an IDE. The defaults are ugly, but they're perfectly functional.

None of these people customized Notepad or any other editor either. They're simply solving the problem of "I've SSH'ed into a remote machine. How do I edit this file?"

zelphirkalt 6 hours ago [-]
Why are they then even setting up vanilla Emacs in particular? Not impossible, of course, but it seems like a strange choice to make when not wanting to engage with its configuration. Or maybe another question would be who set them up with vanilla, unconfigured Emacs, if they are not so technical users? Even discovering tramp is not trivial or automatic and for working on a remote machine, isn't Vim much more common?
BeetleB 2 hours ago [-]
Emacs is just there in their environment. They didn't set anything up.

> Even discovering tramp is not trivial or automatic and for working on a remote machine, isn't Vim much more common?

Full confession: I, as a power Emacs user don't use tramp. It's ok not to.

I know people disagree with me, but for the most basic editing Emacs has virtually no learning curve compared to Vim. You just open the file and start typing like you would any other editor.

pjmlp 17 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 days 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.
so-cal-schemer 3 hours ago [-]
Here is a project using Scheme via GUIX to manage reproducible scientific workflows in high-performance computing (HPC). There are installations using it in France, Germany, The Netherlands, Australia, The United States.

https://hpc.guix.info/about/

https://hpc.guix.info/blog/2026/02/guix-hpc-activity-report-...

Guix-HPC started as a joint software development project involving three research institutes: Inria, the Max Delbrück Center for Molecular Medicine (MDC), and the Utrecht Bioinformatics Center (UBC). Guix for HPC and reproducible research has since received contributions from many individuals and organizations, including CNRS, Université Paris Cité, the University of Tennessee Health Science Center (UTHSC), Cornell University, and AMD. HPC remains a conservative domain but over the years, we have reached out to many organizations and people who share our goal of improving upon the status quo when it comes to software deployment.

zelphirkalt 13 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.

so-cal-schemer 3 hours ago [-]
Would love to get coffee or kölsch sometime.
throwaway27448 1 days ago [-]
Racket has been able to produce standalone executables for a while now.
gus_massa 1 days 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 days ago [-]
For years, really.
velcrovan 18 hours ago [-]
Not only do I produce standalone executables in Racket, I cross-compile GUI apps written on MacOS to produce executables for Windows.
dented42 1 days ago [-]
Given that a lot of people use it in the wild I’m not sure where you’re getting that from.
CoreformGreg 6 hours ago [-]
We use it - specifically we use its Scribble package to define self-documented file formats that autogenerate C++ & Python sources.
mark_l_watson 23 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.
TheGoddessInari 1 days ago [-]
hyperbolablabla 12 hours ago [-]
Naughty Dog uses Racket for all their scripting...
so-cal-schemer 2 hours ago [-]
Here is a project started in 2020 to reverse engineer the runtime and language which has playable games now:

https://opengoal.dev/

https://opengoal.dev/blog/progress-report-sept-2020

The OpenGOAL project is a project to reverse engineer the Jak and Daxter video games and the custom programming language they were developed in: GOAL.

The long-term objective of the project is to make Jak and Daxter run natively on a PC by decompiling and recompiling it with OpenGOAL, our reverse engineered version of GOAL for x86-64 PCs. We do not host any game assets or copyrighted material from the game – we only host tools for reverse engineering and porting the game. The user must provide a copy of the game to use our tools.

so-cal-schemer 2 hours ago [-]
Naughty Dog was a great example of how productive Scheme can be in the hands of a few expert programmers. Founders attended MIT and CMU.

GOAL and GOOL were used in Jak and Dexter, Crash Bandicoot, and apparently brought back in The Last of Us:

https://en.wikipedia.org/wiki/Game_Oriented_Assembly_Lisp

Unsynced: The Last of Us Melee System

https://www.youtube.com/watch?v=Ox2H3kUQByo&t=2260s

In this 2014 GDC session, Naughty Dog's Anthony Newman shares an overview of the design and implementation of the melee system for The Last of Us.

em-bee 1 days 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/
so-cal-schemer 46 minutes ago [-]
Some games to play:

https://spritely.institute/arcade/

The underlying tech might be more fun:

https://spritely.institute/hoot/

Hoot is a Spritely project for running Scheme programs on the web, featuring a Scheme to Wasm compiler and a general-purpose Wasm toolchain.

https://spritely.institute/goblins/

https://files.spritely.institute/papers/spritely-core.html

At the heart of Spritely is Goblins, its distributed object programming environment. Goblins provides an intuitive security model, automatic local transactions for locally synchronous operations, and an easy to use and efficient asynchronous programming interface for encapsulated objects which can live anywhere on the network. Its networking model abstracts away these details so the programmer can focus on object programming rather than protocol architecture.

Goblins also integrates powerful distributed debugging tools, and a process persistence and upgrade model which respects its security fundamentals.

LasEspuelas 1 days ago [-]
Zambyte 1 days ago [-]
One of the more notable apps originally written in Racket is https://news.ycombinator.com.

See: https://en.wikipedia.org/wiki/Arc_(programming_language)#His...

so-cal-schemer 40 minutes ago [-]
And for the uninitiated:

https://paulgraham.com/avg.html

In the summer of 1995, my friend Robert Morris and I started a startup called Viaweb. Our plan was to write software that would let end users build online stores. What was novel about this software, at the time, was that it ran on our server, using ordinary Web pages as the interface.

A lot of people could have been having this idea at the same time, of course, but as far as I know, Viaweb was the first Web-based application. It seemed such a novel idea to us that we named the company after it: Viaweb, because our software worked via the Web, instead of running on your desktop computer.

Another unusual thing about this software was that it was written primarily in a programming language called Lisp. It was one of the first big end-user applications to be written in Lisp, which up till then had been used mostly in universities and research labs. ...

dexterlagan 15 hours ago [-]
For my own tools, some of them still in production at home and at work: https://github.com/DexterLagan?tab=repositories&language=rac...
velcrovan 18 hours ago [-]
eru 1 days ago [-]
You can ask Claude to write you some.
so-cal-schemer 19 minutes ago [-]
Are you suggesting, possibly, that a team of 10x Lisp hackers with 10x agent force multipliers could catch up with the depth and bread of Python's CheeseShop and proceed to right the world?
mono442 1 days ago [-]
it's an academic language
so-cal-schemer 24 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

spike021 1 days 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).
mcbk2142 1 days ago [-]
Very cool! Always was interested in racket, I will try writing Black Jack in it!
spdegabrielle 23 hours ago [-]
vermilingua 23 hours ago [-]
Another awesome Racket resource: https://beautifulracket.com/
0x3444ac53 1 days ago [-]
vatsachak 1 days ago [-]
I've never seen the appeal in schemes other than hot reloadability...
so-cal-schemer 1 days 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 17 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 16 hours ago [-]
Absolutely. You can thank Lisp for paving the way. And in a way, those languages are part of the extended family.
1 days ago [-]
WalterGR 1 days ago [-]
Homoiconicity.
4 hours ago [-]
chowells 1 days 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?

gus_massa 1 days 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 1 days 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 1 days 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

spdegabrielle 24 hours ago [-]
GregBuchholz 22 hours ago [-]
Is Prolog bicameral? Because it has a `read` predicate? Is bicameral then just a matter of implementing a `read` function which returns a tree of tokens, which you could implement for C or Python? Which makes bicamerality a property of a library rather than a property of the language?
WalterGR 1 days 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...

so-cal-schemer 1 days 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 1 days ago [-]
Also, CRUNCH from Chicken Scheme:

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

so-cal-schemer 23 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.

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
WalterGR 24 hours ago [-]
Fair point.
chowells 1 days 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 1 days 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 22 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 22 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)

bjoli 1 days 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 days 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 13 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.

eru 1 days ago [-]
C has homoiconicity: you can represent C source code as C strings.
so-cal-schemer 23 hours ago [-]
You can show this is possible, but it is extremely onerous in comparison.
so-cal-schemer 14 hours ago [-]
Or not. Not even as a Turing Complete language.

Homoiconicity is a fundamental language property, not an algorithm nor an implementation issue. Lisp implemented in Pascal is still homoiconic. C is not homoiconic, whether the compiler is implemented in C or in Pascal, and C cannot be made homoiconic without adding fundamentally new and different constructs to the language definition.

Further sources:

https://wiki.c2.com/?HomoiconicLanguages

https://wiki.c2.com/?HomoiconicExampleInManyProgrammingLangu...

23 hours ago [-]
so-cal-schemer 23 hours ago [-]
Zambyte 1 days ago [-]
You can't natively index into a string and get any C syntactic token out of it besides a char.
eru 21 hours ago [-]
You can write some library functions to get you more sophisticated indexing, if that's what you want.
Zambyte 16 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.
jibal 1 days 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 21 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 19 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 18 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 15 hours ago [-]
[dead]
jibal 5 hours ago [-]
> What does 'within the language' mean?

It's obvious, and the rest of the comment confirms that:

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

I already answered this, so this is clearly bad faith trolling/sealioning:

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

layer8 1 days ago [-]
I don’t find it appealing when everything looks the same irrespective of purpose and context.
eru 1 days ago [-]
Seems like a non-sequitur.
layer8 1 days 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 21 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).

BoingBoomTschak 1 days 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.
jibal 1 days 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 1 days 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.

infinitebit 19 hours ago [-]
The second recursion example introduces 2 syntax features without explaining them (let, square brackets)
ux266478 1 days 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.

so-cal-schemer 4 hours ago [-]
If anybody reading this branch of the discussion wants to investigate Logic Programming and Nondeterministic Computing further in the context of Racket/Scheme, it is discussed in §4.4 of SICP as a Scheme variant. A variant created by modifying the meta-circular evaluator -- as Ableson says, the real superpower of Lisp, and an excellent display of the usefulness of homoiconicity.

4.4 Logic Programming

https://sarabander.github.io/sicp/html/4_002e4.xhtml#g_t4_00...

4.3 Variations on a Scheme — Nondeterministic Computing

https://sarabander.github.io/sicp/html/4_002e3.xhtml#g_t4_00...

4.1 The Metacircular Evaluator

https://sarabander.github.io/sicp/html/4_002e1.xhtml#g_t4_00...

so-cal-schemer 22 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 20 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 17 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

so-cal-schemer 4 hours ago [-]
Great point for the timeline.

more: https://wiki.c2.com/?PascalLanguage

BoingBoomTschak 24 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 20 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 14 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 7 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"?

BoingBoomTschak 7 hours ago [-]
That link wasn't an argument to support my claim, just a related comparison.

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

Big disclaimer: I have almost no Prolog experience beyond a distant university class, so I've no idea what's possible with a pragmatic impl. like SWI-Prolog. Still, this is my uninformed "Emacs vs vim tier" opinion.

For the same reason as most other people do so: when you're not working with problems suited for it (most of them), Prolog loses much of its power and intuitiveness to become a barebones functional PL without much going for it (needing lots of extensions beyond ISO); bit like Erlang outside distributed/high availability situations. The other way is going full "hammer and nails" and try to hamfist every problem into a logic/declarative one.

YeGoblynQueenne 6 hours ago [-]
That's not my experience of Prolog and I can't see how it's yours if you have "almost no Prolog experience" etc.

EDIT:

I don't want to get into a holy flamewar (vim vs emacs? Oh dear) but I've heard that criticism before. I think the reason is that there's a kind of program that is absolutely trivial to write in Prolog: business logic, rules engines, etc, anything where you can write out the program logic as a set of facts and rules. I mean duh. A Prolog program is a set of facts and rules. But at that point you're* not even trying to use the language. Then of course, if you try to do something more complicated it will take more effort and you will need to really understand how Prolog works; which is not trivial.

So I think it's a bit like mathematics. Yeah, if you just want to add 1 and 1, it's easy. If you want to calculate the amount of fuel it will take to fly to the moon and back, it takes considerably more effort. You can see it as maths being "easy for the easy things but hard for the hard things", but it's just that the hard things are hard and take more work, not the fault of maths as such.

__________

* "You" in the abstract, not you personally.

srcreigh 20 hours ago [-]
Side note, does anybody know of some good Rhombus codebases I can read?
zelphirkalt 11 hours ago [-]
Fairly new, I think there will not be much Rhombus around yet.
ventana 23 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.
tech_army 1 days ago [-]
Will definitely try it.
1 days ago [-]
SilentM68 1 days ago [-]
Thanks for sharing. It's been bookmarked :)

  .-.
 / λ \
 '---'
usxr1515 1 days 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.
2 hours ago [-]
so-cal-schemer 2 hours ago [-]
Some resources on implementing Scheme and variations:

Essentials of Programming Languages 3e

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

Essentials of Compilation: An Incremental Approach in Racket

https://github.com/IUCompilerCourse/Essentials-of-Compilatio...

Lisp in Small Pieces

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

brabel 24 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 20 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

tonetheman 1 days ago [-]
I have tried racket and it was fun enough but man those parens.

But racket is a really nice language to play with.

so-cal-schemer 2 hours ago [-]
There are tools, particularly packages in Emacs, but also for VS Code and other IDEs, that make navigating nested s-expressions a breeze. And other tools for balancing parens automatically, and for syntax highlighting. You can just dim them if they are too visually jarring.
Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact
Rendered at 22:21:46 GMT+0000 (Coordinated Universal Time) with Vercel.