Live data from Hacker News

Understanding the Power of Lisp (2020)

joshbradley.me

61–70 of 140 posts

Re: Understanding the Power of Lisp (2020)

#61
post #38

Earlier quoted context omitted.

When I first started writing asm code, it was accepted to write self-modifying code (programmatically create new code, then jmp to it.) Then I discovered and was told it was a bad idea. So that's how I view Lisp macros - creating code on the fly and executing it. When I was studying this issue, I came across femtolisp, which is a standalone Lisp by the author of Julia, Jeff Bezanson. Julia has metaprogramming so I wa…

> So that's how I view Lisp macros - creating code on the fly and executing it. This is an incorrect assumption. In compiled Lisps[0], macros are a compile-time construct, which manipulate the data structures that represent your code[1]. All manipulation occurs at compile-time. In interpreted Lisps, macro evaluation is temporally intertwined with program execution, but the two phases are logically distinct. [0]: Comp…

So Clojure programs are data structures, and you can pass a structure to a macro to rewrite it into something the compiler can evaluate. In this way you can extend the language. You can build your own DSL that solves your particular problem in an organic way that grows as you go along.

If I understand that correctly I don't get it, because I would still have to write the macro, which in C# I would write as a function that I call in my source code, which doesn't grow the syntax of C#, but grows the program to a DSL that solves my problem, albeit not as nicely as in a REPL.

Maybe this is all about the REPL and I don't use them or see any appeal in them. I like to write my text, look at it, and hit run. You write a REPL line and it disappears.

I'll keep learning. I'm sure the light bulb will go off eventually.

Re: Understanding the Power of Lisp (2020)

#62
post #23

Earlier quoted context omitted.

The author might be new to Lisp, and I hope what I write doesn't discourage him. There are some other mistakes: ((ab . c) . d . nil) isn't a valid s-expression. Maybe it should be ((ab . c) d . nil) Also, (eq '(a b) '(a b)) ; (a b) is a list and cannot be evaluated by eq eq works fine on lists. It returns T iff its arguments are the same object (i.e. are at the same memory location, or are small enough integers or fl…

don't count on (eq '(a b) '(a b)) being NIL. In Common Lisp it can be T. From what I read of Racket, it could be true, too.

If that were true I would consider it a very weird Common Lisp implementation. The main point is that you should never depend on it being true even though the two forms look the same.

A couple more examples for the Common Lispers in the audience that highlight the difference between the reader and the evaluator:

  (setf foo (list #1=(list 3 4 5) #1#))
  (eq (car foo) (cadr foo)) ; --> ??
What should we expect the second form to return? T or NIL?

Answer: It might return true in some implementations (very weird implementations), but in general one should not expect it to do so. Why?

  (setf foo (list #1=(quote (3 4 5)) #1#))
  (eq (car foo) (cadr foo)) ; --> ??
Now what should the second form return?

Answer: It absolutely must return true. Why?

Re: Understanding the Power of Lisp (2020)

#63
post #42
post #21

A fun exercise when reading posts like those is to mentally compare to TCL. It is somewhat uglier - TCL's lists are multi-valued; and newlines are signficicant, introducing difference between "script" and "command". Still, the basic eval command looks remarkably similar in lisp vs TCL.

> Still, the basic eval command looks remarkably similar in lisp vs TCL. The primary difference is whether arguments are evaluated before (Scheme/Lisp) or after (Tcl) being sent to the function/command. The deferred evaluation is what allows you to do things that you need macros for in other languages. (ie. you need deferred evaluation or macros to implement short-circuit operators, for example) However, early Lisps…

If I am reading this right, TCL's approach is neither FEXPRs nor Lisp's macros -- instead, it is a very concise "quote" command and "uplevel" command which is something I have not seen widely used in any other language.

In the lisp terms, imagine a language with no macros and with immediate evaluation ("applicatives" in the Kernel-speak). And no special forms either, except for syntax-level quote command. This means that, for example, for "if" you have to quote the code parameters to make sure they are not evaluated too early:

    (if var '(print '(hello world)) nil)
the naive implementation has an obvious problem with variable scope, which you solve with "uplevel" command which evaluates expression in caller's scope. So there really is no difference in the data vs code, syntax-wise -- the first quote in the example above defines the "code" while second one defines "data", and only the function implementation knows how each argument will be used.

And that's how you build a language with homoiconicity and macro-like facilities, but without FEXPRs or applicatives or special syntax. Granted, it has plenty of downside, but it's still a very interesting approach.

Re: Understanding the Power of Lisp (2020)

#64

LISPs power is it's problem. Why would I want a powerful language? I want a language that has one and only one obvious way to do things. That doesn't require building my own language features or choosing an addon that might or might not be compatible with others. I don't have any use for the flexibility to customize a language because I want a language that doesn't need to be customized. If I was doing cutting edge A…

Enlightenment doesn't come from actually using LISP, I've concluded, after years of feeling dumb about it. It comes from understanding how LISP is built. Once you understand how LISP is built, you'd never actually use it for a serious project.

My key take aways from learning how LISP is built: 1) Code & data can be unified into the same thing. This isn't particularly deep if you know the concept of an AST. 2) You can bootstrap a language from a simplified version of the language. Repeat this a few times and you get a fancy language. This isn't particularly deep if you've built any compiler, or if you've built LaTeX. 3) You can build beautiful things if you disregard the grittiness of reality. LISP offers an abstraction of physical computers that does not at all resemble a real computer. Some people like this abstraction. Maybe because they don't like how computers actually work.

Re: Understanding the Power of Lisp (2020)

#65

LISPs power is it's problem. Why would I want a powerful language? I want a language that has one and only one obvious way to do things. That doesn't require building my own language features or choosing an addon that might or might not be compatible with others. I don't have any use for the flexibility to customize a language because I want a language that doesn't need to be customized. If I was doing cutting edge A…

> Why would I want a powerful language?

You clearly don't, since your aim is to write boring and forgettable code. I don't think there is anything wrong with that -- you can write boring code to solve interesting and worthwhile problems, and overall the tendency is probably to err on the side of getting distracted by hip or shiny tech. But some people enjoy writing interesting code, and sometimes interesting code is needed to solve interesting problems and a powerful language can make certain types of interesting code much less painful to write. Auto-differentiation might be a good example.

Re: Understanding the Power of Lisp (2020)

#66
post #39

Earlier quoted context omitted.

When I first started writing asm code, it was accepted to write self-modifying code (programmatically create new code, then jmp to it.) Then I discovered and was told it was a bad idea. So that's how I view Lisp macros - creating code on the fly and executing it. When I was studying this issue, I came across femtolisp, which is a standalone Lisp by the author of Julia, Jeff Bezanson. Julia has metaprogramming so I wa…

Writing macros in Lisp when you don't have to is also generally discouraged. They're typically harder to write and debug than functions, so their use is best reserved for situations when the opposite would be true.

Exactly this. New Lispers sometimes overuse macros because they're so powerful, but macro overuse can make your code impossible to debug or maintain.

At the same time, when you really need a macro, you really, really need a macro because it can do things a function cannot like controlling when or if its arguments are evaluated.

There are multiple reasons to use a macro rather than a function. I won't describe them here* but suffice it to say that the statement "macros are no longer useful in modern programming" is completely false.

*See pg's _On Lisp_ for an in-depth discussion of macros.

Re: Understanding the Power of Lisp (2020)

#67
post #12

While I don’t disagree with Josh’s blog, concentrating on just language features leaves out the style of Lisp development: bottom up REPL style development. When I have to use Haskell or Python, I find myself working as if I were using Lisp: I still favor the REPL and building up from primitive functions to the top level application. This is probably a bad habit but it is the way I work. BTW, using the standard Emacs…

I feel like the bottom up style is possible in static languages like C++ too, except instead of manually testing small functions in the repl, you can write small unit tests for them instead. In a way it's even better because a test written once can be run many times, while testing in the repl requires a manual test with every change.

I'm guessing the draw is in the immediate feedback. Like iteratively creating a chain of commands in shell pipe. Compiling and then executing typically takes a minimum of a few seconds before you get feedback

Re: Understanding the Power of Lisp (2020)

#68
post #37
post #24

Earlier quoted context omitted.

I've learned Clojure. Tried and failed to see this unique power of macros. Truly asking for help: can you help explain what can I do with macros that I cannot do with functions? Or, maybe, cannot do with high quality or low complexity using functions?

Here's a simple example you can't write in most languages. (first-working (get-it-from-the-cache) (get-it-from-the-database) (get-it-from-an-external-api) (compute-it-the-slow-way) default-value) Note each of those functions could throw an exception. You want to ignore it (or you could log it) and move on to the next.

don't remember the exact scala syntax

  cache.get
    .orElse(db.get)
    .orElse(api.get)
  ...

Re: Understanding the Power of Lisp (2020)

#69

Earlier quoted context omitted.

When I first started writing asm code, it was accepted to write self-modifying code (programmatically create new code, then jmp to it.) Then I discovered and was told it was a bad idea. So that's how I view Lisp macros - creating code on the fly and executing it. When I was studying this issue, I came across femtolisp, which is a standalone Lisp by the author of Julia, Jeff Bezanson. Julia has metaprogramming so I wa…

Self-modifying code isn't a bad idea. Like everything else, it has it's place, just not typically on modern computers due to breaking optimizations. What you describe is actually used in a lot of modern software, usually self-modifying code refers to code that modifies itself while running, typically in a loop, not just generating fresh code :)

In my opinion, compile time codegen is way better than runtime codegen. Unless you are exploring genetic algorithms, runtime codegen is slow, brittle, and undebugable. Compile time codegen, however, can produce ridiculously fast code (see FFTW), and is much easier to debug because you can look at the generated code.

Re: Understanding the Power of Lisp (2020)

#70

I'm programming in Lisp (Chez Scheme) now. I've been a serious Lisp programmer (recreationally and sometimes professionally) for around 10 years and I have programmed in Common Lisp, Scheme, my own weird dialects, Emacs Lisp, etc. At this point I feel like almost everything written about Lisp is silly. Taken as a language family as a whole, there isn't much that separates Lisp from most of the other languages that ar…

> It won't really help you solve hard problems.

I have to take issue with this. There are only two languages I consider when faced with a very hard problem: Common Lisp and Haskell. One can solve easy problems in any language. But when I have to solve a new problem from scratch that nobody's ever solved before, I don't reach for Java or Python. I reach for the languages that augment my brain rather than limiting it.

Post reply on HN