Live data from Hacker News

An Intuition for Lisp Syntax

stopa.io

81–90 of 201 posts

Re: An Intuition for Lisp Syntax

#81

This is maybe the best introductions to Lisp i have seen, especially for a js dev like me it could hardly get more approachable and convincing. I could not help but thinking at the end, this is really awesome but s-expressions are kind of hard to read and reason about for my brain, it would be cool if we could generate them from more readable syntax, maybe something like javascript :D . Unfortunately the standard js…

> I could not help but thinking at the end, this is really awesome but s-expressions are kind of hard to read and reason about for my brain, it would be cool if we could generate them from more readable syntax, maybe something like javascript :D . So many people start out thinking like this when they learn Clojure or some other Lisp and then after like 2-4 weeks of reading Lisp code, any C-like code starts to look co…

Everything is so extremely context sensitive and has much more focus on text over symbols than other languages, which isn't a problem for computers but is a huge problem for humans. Therefore lisp is objectively harder to read for humans. If you have never read other code and is super familiar with lisp it is easier to read, but anyone with experience with both will find other languages much much much easier to read thanks to the much better UX design.

I don't see how you can argue otherwise. It is as if you have no clue at all about how UX works. You can't get used to everything, there is a hierarchy of syntaxes, and lisp syntax is just plain bad for humans.

Re: An Intuition for Lisp Syntax

#82
post #63
post #52

The article and the heading seem to diverge a bit. "An Intuition for Lisp Syntax" is much simpler than all the pages: in lisp, the syntax for control structures like a loop is the same as the syntax for everything else. The reason for that is because introducing special syntax disrupts useful patterns that could be used for writing completely new control structures. Threading macros, for example, are pretty much impo…

Also, haskell has "only" expressions and no statements.

Haskell also has definitions, which can't be used as expressions directly, e.g. we can write:

    foo = bar
But we can't write:

    baz = 2 + (foo = bar)
I this case we can get the expected return value by using 'let... in...' syntax, but we wouldn't get the top-level definition, e.g.

    baz = 2 + (let foo = bar in foo)
Likewise for type definitions, class definitions, etc.

One language which handles definitions nicely as expressions is Nix. Nix has 'let' but I never use it. Instead we can use 'with':

    with {
      foo = bar;
    };
    "hello " + foo
The thing in braces isn't a block of statements; it's a key/value dictionary (Nix calls them 'attribute sets' or 'attrsets'). The 'with...; ...' syntax acts like 'let... in...' but both pieces are expressions.

    with {
      myAttrs = {
        foo = bar;
      };
    };
    with myAttrs;
    "hello " + foo
Interestingly, whilst Lisp distinguishes between 'letrec' and 'let' (bindings with and without mutual/self-reference, respectively), Nix distinguishes between 'rec {...}' and '{...}' (attrsets with and without mutual/self-reference). In other words, when we write 'with rec {...}' the 'rec' modifier affects the definition of the attrset (the '{...}'), it doesn't affect the binding of that attrset into the environment (the 'with'). For example:

    with {
      bar = 42;  // To prevent nonRecursive complaining about a missing variable
      recursive = rec {
        foo = bar + 1;  // The number 6, since 'bar' is taken from this 
        bar = 5;        // The order of definitions doesn't matter
      };  // The attrset { foo = 6; bar = 5; }

      nonRecursive = {
        foo = bar + 1;  // The number 43
        bar = 5;
      };  // The attrset { foo = 43; bar = 5; }
    };
    // [] is syntax for a list
    [
      (with recursive;    foo)  // The number 6
      (with nonRecursive; foo)  // The number 43
    ]  // The list [6 43]
Of course, attrsets (recursive or not) can be used as expressions:

    (rec { foo = bar + 1; bar = 5; }).foo  // The number 6

Re: An Intuition for Lisp Syntax

#83

Earlier quoted context omitted.

> I could not help but thinking at the end, this is really awesome but s-expressions are kind of hard to read and reason about for my brain, it would be cool if we could generate them from more readable syntax, maybe something like javascript :D . So many people start out thinking like this when they learn Clojure or some other Lisp and then after like 2-4 weeks of reading Lisp code, any C-like code starts to look co…

Everything is so extremely context sensitive and has much more focus on text over symbols than other languages, which isn't a problem for computers but is a huge problem for humans. Therefore lisp is objectively harder to read for humans. If you have never read other code and is super familiar with lisp it is easier to read, but anyone with experience with both will find other languages much much much easier to read…

People saying that Lisp is as easy to read since you get used to it is like people saying that salad is as tasty as cake since you get used to it. Sure someone eating cake for the first time after having only eaten non sugary stuff for a long time will find it a bit jarring, but anyone eating both will find cake so much tastier, which is why people overeat cake so much.

Yet many people will still adamantly say that they find salad tastier. And still go on and eat cake and get fat. They are just lying to themselves, I'm not sure why. Lispers are the same way, its just a reality distortion field.

Re: An Intuition for Lisp Syntax

#84

Earlier quoted context omitted.

> This is maybe the best introductions to Lisp i have seen, especially for a js dev like me it could hardly get more approachable and convincing. The classical one is "The Nature of Lisp"[0], which introduces data-as-code through XML and Java build tools. Same idea, just with examples more relevant at the time of writing. Still worth a read for non-webdev programmers. Having learned Lisp, it's half funny, half dishea…

Greenspun's tenth rule [0]: "Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp." Also: > Hacker Robert Morris later declared a corollary, which clarifies the set of "sufficiently complicated" programs to which the rule applies "...including Common Lisp." [0] - https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule

JavaScript (which is based on Scheme dialect of Lisp) is formally specified, well-developed, and fast, so quote above is outdated.

Re: An Intuition for Lisp Syntax

#85

This is maybe the best introductions to Lisp i have seen, especially for a js dev like me it could hardly get more approachable and convincing. I could not help but thinking at the end, this is really awesome but s-expressions are kind of hard to read and reason about for my brain, it would be cool if we could generate them from more readable syntax, maybe something like javascript :D . Unfortunately the standard js…

> s-expressions are kind of hard to read and reason about for my brain I think it's likely this is just a matter of familiarity. Like the way that people think the Windows (/ whatever) GUI is intuitive - but when you test this by putting someone who has never used it in front of a screen, it actually isn't. "What do you mean if I write something and don't also 'save' it, whatever that is, it will disappear? This is s…

That becomes a no true Scotsman fallacy though. People argue "Either you like lisp syntax or you haven't used it enough to have a valid opinion". That is a bullshit argument.

Re: An Intuition for Lisp Syntax

#86
post #62

Earlier quoted context omitted.

If you buy into the argument that less code, that writing code at a higher level of abstraction results in less time to market, less maintenance overhead, more responsiveness to change in future, less bugs etc. Etc. If you believe a 1000 line program is worse than a readable 100 line program that both do the same thing, then lisp with its macros is that thing. Super expressive and readable. I went from not being able…

The argument isn't whether less, readable, higher-abstraction code is better than its opposite, it is. It's whether or not a real-world LISP code-base espouses these qualities. Which I don't believe it does, IMO the primary reason LISP isn't mainstream is because it results in less readable code for humans (i.e. the primary objective of programming languages), it's semantically the perfect minimalist language for a m…

>IMO the primary reason LISP isn't mainstream is because it results in less readable code for humans

I disagree. Being a mathematician LISP just feels natural and the ugliest, most horrible and hard to read language by far is java (IMO, of course). But that's just probably because all my life I've been thinking about functions and not objects.

If your background is OOP and look at some LISP there's no surprise if it looks unreadable, even more so if you have no real motivation to understand it.

Re: An Intuition for Lisp Syntax

#87

Can anybody recommend resources for the "next level"? I get that "code is data", the "unless" example is nice, but what I don't get is: why would I want to do that? As a non-LISP developer I get by without macros and all the fancy things that are possible with LISP. I would like to see a strong case of daily tasks that are much easier with LISP macros. Anybody?

Macros are useful for defining things that don't make sense "within" our programming language. For example, I write a lot of Scala code, and that language has a bunch of types like 'Tuple2[A, B]', 'Tuple3[A, B, C]', 'Tuple4[A, B, C, D]', etc. As a programmer we can see that these are all instances of some higher-level pattern 'TupleN[T1, T2, ..., TN]' but Scala has no idea about that; hence we can't write code which, for example, sums the first N elements of any tuple.

Actually, we can but it's not safe: Scala's tuples implement a 'Product' interface which we can use to loop through the values, but this loses a lot of type safety (looking up values via Product will upcast them to type 'Any'; our function's signature will ask for a 'Product' rather than specifically for tuples of numbers of at least a certain length, etc.)

Macros generate code at each of their call sites, which lets us automatically translate our 'higher level language' (e.g. 'Tuples of different lengths') into the 'real language' (e.g. Scala). For example, we can have a macro that translates 'tupleSum(5, 10, foo)' into a call like 'tupleSum5_10(foo)', where the function 'tupleSum5_10' is hard-coded to sum the first 5 elements of a Tuple10 (and is therefore acceptable to Scala, since it doesn't try to abstract over tuple lengths), e.g.

    def tupleSum5_10[T1, T2, T3, T4, T5](t: Tuple10[Int, Int, Int, Int, Int, T1, T2, T3, T4, T5]): Int =
      t match {
        case (x1, x2, x3, x4, x5, _, _, _, _, _) => x1 + x2 + x3 + x4 + x5
      }
Of course, we don't want to be writing functions like 'tupleSum5_10' manually. Hence we can use another macro to define these functions! For example, the above might be generated by a macro call like 'defTupleSum(5, 10)'. If we run this macro in a loop, we can define all of the sum functions for tuples up to, say, Tuple20.

If we provide this in a library, users can treat tuples in a more high-level way. Since each macro call just generates some boilerplate Scala code, the result will be type-checked, etc. to make sure we've not done anything dodgy, e.g. since a call like 'tupleSum(5, 10, foo)' expands to 'tupleSum5_10(foo)', Scala will check whether 'foo' matches the type 'Tuple10[Int, Int, Int, Int, Int, T1, T2, T3, T4, T5]'.

I think the most famous use of macros in Scala is Shapeless https://github.com/milessabin/shapeless/wiki/Feature-overvie... (although I've not personally used it)

Note that the exact same problem with tuples happens in Haskell too, which can also be worked around using Haskell's macro system (TemplateHaskell).

Re: An Intuition for Lisp Syntax

#88
post #62

Earlier quoted context omitted.

If you buy into the argument that less code, that writing code at a higher level of abstraction results in less time to market, less maintenance overhead, more responsiveness to change in future, less bugs etc. Etc. If you believe a 1000 line program is worse than a readable 100 line program that both do the same thing, then lisp with its macros is that thing. Super expressive and readable. I went from not being able…

The argument isn't whether less, readable, higher-abstraction code is better than its opposite, it is. It's whether or not a real-world LISP code-base espouses these qualities. Which I don't believe it does, IMO the primary reason LISP isn't mainstream is because it results in less readable code for humans (i.e. the primary objective of programming languages), it's semantically the perfect minimalist language for a m…

>> the primary reason LISP isn't mainstream is because it results in less readable code

It’s fresh in my head just how unreadable i found Clojure when browsing source of random projects on github a couple of weeks ago. Its not that penetrable to begin with.

However, that was only a couple of weeks ago, since then i’ve come to really appreciate the simple magic of lisp syntax.

I’ve also been able to put it to practical use rather than just appreciating it - i’ve been spoiled by Calva in VS Code, structural editing that doesn’t just work, it actually flows - i’m no stranger to structural find and replace in Intellij but this is different, this smoothes an impedance mismatch between our thinking and expressing code.

Re: An Intuition for Lisp Syntax

#89

This is maybe the best introductions to Lisp i have seen, especially for a js dev like me it could hardly get more approachable and convincing. I could not help but thinking at the end, this is really awesome but s-expressions are kind of hard to read and reason about for my brain, it would be cool if we could generate them from more readable syntax, maybe something like javascript :D . Unfortunately the standard js…

> s-expressions are kind of hard to read and reason about for my brain I think it's likely this is just a matter of familiarity. Like the way that people think the Windows (/ whatever) GUI is intuitive - but when you test this by putting someone who has never used it in front of a screen, it actually isn't. "What do you mean if I write something and don't also 'save' it, whatever that is, it will disappear? This is s…

Maybe the last sentence needs a bit expanding, I think most readers already use a powerful text editor and don't see how standard features would help with lisp. The thing is, you don't really edit Lisp as text. Two commonly used extensions (for emacs, vim, vscode, sublime...) are paredit and rainbow. Those extensions kick in when lisp code is detected.

It then feels more like tree manipulation than like writing code. Braces are always matched, you literally cannot delete the closing brace using normal text editing. Each tree depth as its own brace color so it's easy to distinguish between them.

Restructuring tree to join two blocks of code or to extract block to higher level is exposed as command mapped to keyboard shortcut. It's not semantic editing in sense that extension is aware what is function and what is data, you can still make nonsense code. Still, reading and manipulating code is quite effortless.

Re: An Intuition for Lisp Syntax

#90
post #79

Can anybody recommend resources for the "next level"? I get that "code is data", the "unless" example is nice, but what I don't get is: why would I want to do that? As a non-LISP developer I get by without macros and all the fancy things that are possible with LISP. I would like to see a strong case of daily tasks that are much easier with LISP macros. Anybody?

Macros are in practice a tool to reduce boilerplate. Which is why they are typically written for libraries and used in application code. For example parametrization and modularization are used to keep code DRY and separated at a functional level. However this can only get you as far as you still write ceremonial repetition around using those functions/modules/objects. Macros can get your code to that extra level of b…

I've also used macros to execute some expensive data processing at compile-time. I had a fixed dataset containing named sets of strings, and I wanted a program which reads in a set of strings and returns the names of any supersets which appear in the dataset. For example, if the dataset contains 'x = {a, b, c}', 'y = {a, b, d}' and 'z = {a, c, d, e}' then a query of '{a, c}' should return '{x, z}'.

I managed to speed this up a lot by using a (TemplateHaskell) macro to (a) read in the dataset from an external file at compile time, (b) transform it into a structure that has fast lookups and (c) serialise that structure to a Haskell syntax tree. Not only did this make lookups faster at runtime, it also eliminated all the error checking for locating/reading/parsing/etc. of the dataset: any problem would cause a compile error; if compilation succeeded, the resulting program didn't have to know or care about any of that.

Post reply on HN