[HN Gopher] A different take on S-expressions
___________________________________________________________________
A different take on S-expressions
Author : tearflake
Score : 47 points
Date : 2025-06-15 11:25 UTC (3 days ago)
(HTM) web link (gist.github.com)
(TXT) w3m dump (gist.github.com)
| phoe-krk wrote:
| Lisp programmer here.
|
| Traditional S-expressions, by their definition, ignore most of
| whitespace; additionally, reading sexprs is always a linear
| operation without the need to backtrack by more than one
| character.
|
| The suggestion from this post violates both assumptions by
| introducing a 2D structure to code. To quote this post's
| examples, it requires the multiline string in
| (fst-atom """ trd-atom) 00001
| 00002 00003 """
|
| to be fully read before TRD-ATOM. It also forces the reading
| function to jump up and down vertically in order to read the
| structure in * (
| ) * e ( ) ( ) * q
| m ( ) p ( ) * u a a o a
| 2 * l w *
|
| The author also states that (eq (mul (a a))
| (pow (a 2)))
|
| is less readable than * (
| ) * *eq* ( ) ( )
| * *mul* ( ) *pow* ( ) *
| *a* *a* *a* *2* *
| *
|
| Then there's the ending passage:
|
| _> we hope that the introduced complexity is justified by the
| data readability expressed this way._
|
| I cannot force myself to read this post as anything but a very
| poor Befungesque joke.
| tgv wrote:
| A normal tree would be easier to read
| eq mul pow a a a 2
| derriz wrote:
| Turned 90, maybe? eq: mul:
| a a pow: a 2
| hyperhello wrote:
| x*x == pow(x,2)
| derriz wrote:
| We have a winner!
|
| Actually, I'd suggest a slight improvement: x*x = x^2
| xigoi wrote:
| x * x = x2
| unstruktured wrote:
| Thanks for restoring my sanity. Was quite confused of the value
| added by the author.
| tearflake wrote:
| Sorry for the confusion. I must be a very disturbed person
| because I kind of like what is explained there.
| velcrovan wrote:
| It gets worse/better. Since Racket allows you to hook your own
| reader in front of (or in place of) the default reader, you can
| have things like 2D syntax: #lang 2d racket
| (require 2d/match) (define (subtype? a b)
| #2dmatch +----------+----------+-------+----------+
| | a b | 'Integer | 'Real | 'Complex |
| +----------+----------+-------+----------+ | 'Integer
| | #t |
| +----------+----------+ | | 'Real
| | | | +----------+
| +-------+ | | 'Complex | #f |
| | +----------+------------------+----------+)
|
| https://docs.racket-lang.org/2d/index.html
| f1shy wrote:
| Yes that part must be a joke!
|
| I've seen dozens of attempts to make S-Exp "better" even the
| original M-Exp. I also did some experiments myself. But at the
| end, I come back to goo'ol s-exp. Seems to be a maximum (or
| minimum) found just perchance.
| tearflake wrote:
| Here is another example, an axiom from propositional logic:
| (impl (impl p (impl q r)) (impl (impl p q) (impl p r)))
|
| which, vertically indented in a transposed block, looks like
| this: * (
| ) * i ( ) ( )
| * m i p ( ) i ( ) ( ) p
| m i q r m i p q i p r l p m
| p m m * l p l
| p p * l l
| l *
|
| which, using transposed lines within the transposed block,
| finally looks like this: * (
| ) * *impl* ( ) (
| ) * * *impl* *p* ( )
| *impl* ( ) ( ) *
| *impl* *q* *r* *impl* *p* *q* *impl* *p* *r*
| *
|
| This time I won't make any judgements. Could be good, could be
| bad, you decide.
| exeldapp wrote:
| Not sure if that example helps. You can make any programming
| language hard to read without some basic formatting. The way
| I would write the sexpr would be: (impl
| (impl p (impl q r)) (impl
| (impl p q) (impl p r)))
|
| It's clear when each section begins and ends and doesn't
| require complex parsing rules.
| stray wrote:
| For very large values of "somewhat peculiar"...
| tearflake wrote:
| Changed the "somewhat" to "very" in the document, thank you.
| TOGoS wrote:
| This is fine and interesting, but what I think is lacking in
| S-expression isn't funky vertical syntax, but a way to directly
| represent objects that are not lists. Otherwise one needs to
| invent some representation on top of S-expressions (and then a
| list isn't necessarily a list anymore; everything goes through an
| additional layer of encoding/decoding, losing the elegance of
| S-expressions), or use some extension syntax (usually involving
| '#'), which varies from language to language and might not even
| be interpreted by the reader (but logically expand to some list
| expression that needs to be interpreted again later, so you're
| not really any better off than with the first approach).
|
| I kind of want something like, to borrow JSON-like syntax and
| gloss over namespacing issues: (foo .
| {type: listy-cons-cell head: bar tail: (baz
| quux)})
|
| ...which would be another way to say (foo bar baz quuz), but
| would make it possible to represent any data structure you like
| at the same level as atoms, strings, and lists.
| drob518 wrote:
| See Clojure's reader syntax:
| https://www.clojure.org/reference/reader
|
| You can have vectors, hash maps, and sets in addition to lists,
| symbols, and keywords.
| kgwxd wrote:
| I don't get why anyone even tries after Clojure. They got it
| 100% right. It's easier to read than anything else, and still
| super simple to parse. Commas are whitespace, use them or
| don't, where ever you want. Namespaced keywords are great.
| The data structures themselves act as functions. It's just...
| done.
| sparkie wrote:
| Kernel has first-class environments which aren't just lists,
| but can be constructed from lists. Environments are
| encapsulated, so we can't simply peek into them with car and
| cdr - we can only obtain the value associated with a given
| symbol by evaluating the symbol in that environment.
| ($define! foo ($bindings->environment
| (bar "Hello World") (baz 1234)
| (qux #f))) ($remote-eval bar foo)
| ==> "Hello World" foo
| ==> #[environment]
|
| We could perhaps make something a bit more friendly. Lets
| create an encapsulated `struct` type which could give us the
| contents as a plain list, or let us look up each field:
| ($provide! ($struct struct? destruct $get)
| ($define! (struct-intro struct? struct-elim)
| (make-encapsulation-type))
| ($define! destruct ($lambda (struct)
| (cdr (struct-elim struct)))) ($define!
| $get ($vau (struct member) e
| ($let ((record (car (struct-elim (eval struct e)))))
| (eval member record))))
| ($define! zip ($lambda (keys values)
| ($if ($and? (null? keys) (null? values))
| () (cons (list (car keys) (car
| values)) (zip (cdr keys) (cdr values))))))
| ($define! $struct ($vau kvpairs env
| ($let* ((keys (map car kvpairs))
| (values (map ($lambda (pair) (eval (cadr pair) env)) kvpairs))
| (record (apply (wrap $bindings->environment) (zip keys
| values)))) (struct-intro (cons record
| values))))))
|
| Example usage: ($define! foo
| ($struct (bar "Hello World")
| (baz (+ 12 43)) (qux #f))) ==>
| #inert (struct? foo)
| ==> #t (pair? foo) ==> #f
| (environment? foo) ==> #f
| (destruct foo) ==> ("Hello World" 55 #f)
| ($get foo bar) ==> "Hello World" ($get
| foo baz) ==> 55 ($get foo qux)
| ==> #f ($get foo foo) ==> ERROR:
| Unbound symbol: foo foo
| ==> #[encapsulation]
|
| Kernel: https://web.cs.wpi.edu/~jshutt/kernel.html
|
| Klisp (essentially complete implementation of Kernel):
| https://github.com/dbohdan/klisp
| fn-mote wrote:
| Related but not the same at all, Racket has a 2D syntax (add on
| mode) that gives a different way to program tables where the
| output depends on two different inputs.
|
| https://docs.racket-lang.org/2d/
| jazzyjackson wrote:
| I'll piggyback with my gruesome JSONification of S-expressions. I
| kinda liked having two kinds of braces [straight] and {curly} to
| differentiate arrays and objects, and I did have a event-loop-
| based "parallel" scheduler working to process a tree as soon as
| prerequisites were fulfilled. I might pick up the old project
| again someday, I just got hung up on how I wanted to handle error
| bubbling.
|
| With a vertical script like japanese you could easily rotate the
| whole program 90 degrees to the right (as shown at the bottom of
| the landing page)
|
| https://web.archive.org/web/20240904091932/https://lookalive...
| { "#!join": [ [ "A triangle with side of
| ", "#& side", " and base of ", "#&
| base", "has a hypotenuse of", {
| "#!sqrt": [ [ {
| "#!sum": [ [
| "#!multiply side side", "#!multiply base
| base" ] ] }
| ] ] } ] ] }
| drob518 wrote:
| As a Lisp programmer, just no.
| tearflake wrote:
| Thank you for the criticism. Lots of lispers share your
| opinion.
| chc4 wrote:
| These definitely are extensions that you could add to
| S-expressions, no one can disagree there.
| somewhereoutth wrote:
| dispense with the parentheses: (eq (mul (a a))
| (pow (a 2)))
|
| becomes eq mul a a pow
| a 2
| danielrico wrote:
| Lispython
|
| I should trademark this name.
| tmtvl wrote:
| That's Wisp, I don't care for it, but people who really like to
| assign semantic meaning to precise counts of invisible
| characters may find it interesting.
| derriz wrote:
| Absolutely no counting is required at all so I think your
| joke falls a little flat.
|
| Our visual system has the ability to detect implied straight
| lines (and other simple geometric outlines) from very small
| clues.
|
| Therefore "seeing" the vertical lines implied by the
| indentation is effortless - so it's immediately obvious which
| elements belong to each other.
|
| Indentation is an incredibly valuable "brain" hack that
| manages to instantly communicate hierarchy, not something to
| be sneered at.
|
| We have no such innate ability to match parenthesis -
| determining hierarchy in a jumble of open and close
| parenthesis requires precise counting or, typically these
| days, tool/editor/IDE support.
| thaumasiotes wrote:
| > Absolutely no counting is required at all so I think your
| joke falls a little flat.
|
| Really? How do you see the difference between "TAB" and
| "SPACE SPACE TAB"?
| xigoi wrote:
| The solution to that is easy: do not use tabs.
| XorNot wrote:
| I also don't know why this is treated as controversial
| either: the first thing every project does is declare a
| canonical code formatting aka whitespace layout and start
| rejecting patches which don't follow it.
| agumonkey wrote:
| there's a hybrid form (sweet-expressions ? i forgot), top-level
| terms are parens-free eq (mul a a)
| (pow a 2) defun min (a b) (if (a < b) a
| b)
|
| IIRC the hack to support this at read time was minimal, and it
| made a big impact in terms of "mainstream appeal"
| pkilgore wrote:
| Is this.... is this a joke?
| tearflake wrote:
| I don't intend to be funny. Just a bit childish, but in a good
| way :)
| davesque wrote:
| This is bonkers and I love it.
| tearflake wrote:
| Ikr? People should loosen a bit, why should everything be so
| serious?
___________________________________________________________________
(page generated 2025-06-18 23:00 UTC)