Live data from Hacker News

A road to Lisp: Why Lisp

scotto.me

261–270 of 322 posts

Re: A road to Lisp: Why Lisp

#261
post #244

> After getting comfortable reading code with so many parentheses I never managed to get over the (). Ruby has a very flexible syntax, compared to many other languages, in that you can omit syntax in many cases. For instance, using () for method calls is largely, for the most part, optional. So when I have the python code: cat = Cat() cat.meow() I find it worse than the ruby code: cat = Cat.new cat.meow (Though you c…

> the ruby code: ``` cat = Cat.new cat.meow ``` As a non-Ruby user, this confuses me. Why can't `Cat.new` be a function reference? Does Ruby explicitly disallow this (i.e. passing around a function)?

You can do this, but there's no syntax. Instead, call the `method` method with `:new` as its argument.

Re: A road to Lisp: Why Lisp

#262
post #258

Personally, I grew beyond this. I tried Common Lisp, Forth and Haskell. I enjoyed books On Lisp and Let Over Lambda. Now I think the best programming language is Barry Jay's Triage Calculus, which is close to combinatory logic or untyped lambda calculus. But unlike lambda calculus (which is easily expressed in TC), triage calculus has a built-in quoting and introspection (similar to Lisp's CAR and CDR), which lets yo…

Do you mean Tree Calculus? https://treecalcul.us

Re: A road to Lisp: Why Lisp

#263
post #249

Earlier quoted context omitted.

And it remains in a state of almost getting to the point of generic collections (like C++, Clojure, many others) using the standard functions, but not quite getting there. There are functions (not generic functions) which operate on sequences, but no standard way to extend what types are considered sequences (as one example). It makes sense that the 1995 version of the spec would be incremental, but without a further…

I don't quite understand what you mean by generic collections in this case. Do you want to restrict a list to only contain one specific type?

I wrote that quickly and should have edited it for clarity. What I meant was a new collection type that could be treated as a sequence.

https://www.lispworks.com/documentation/HyperSpec/Body/17_a....

None of the functions operating on sequences are generic, so you cannot create your own type and do a `defmethod` and have them automatically adapt to it. And unlike `print-object` which gets used by format and print and others, there's also no "protocol" (or whatever term you like) generic function you can implement which will make your collection acceptable as a sequence.

You could build something like this, and maybe shadow the standard functions so that it's not a new set of functions from a user perspective, but it's a notable absence in a language with a rich OO system through CLOS. It makes sense that it wasn't in the original spec, but I still think it would have been something that would have been added if the development of CL (as a standard) had continued.

Re: A road to Lisp: Why Lisp

#265
post #238

Earlier quoted context omitted.

> Homoiconicity doesn't give you anything special if your language can parse itself and can eval code. It just makes these completely abstract implementations simpler. Well, in a way it does give you something: By making expression of things like macros simpler, it makes them sometimes worthwhile, and makes it a reasonable request to have this kind of meta programming in your language at all. Without homoiconicity su…

I mentioned in other comment that homoiconicity doesn't necessarily make writing macros simpler. It makes writing trivial toy examples simpler. But let's compare it to a modern macro system like rust's or scala's, where you get a typed object representation of the AST, and for anything non-trivial you are better off with this latter. Also, arguably the best is to have certain features in the language itself, that can…

> Also, arguably the best is to have certain features in the language itself, that can be used to build proper abstractions - so you don't have to resolve to using macros in its place.

I don't agree, because that would mean, that the language must be huge, or grow huge over time, or alternatively be extremely abstract at its core, to be able to fit every use-case. Furthermore, so far I have not seen a language, in which the language designers managed to pull it off, so I tend to think that what already has been successfully pulled off, which is macros for language extensions, is the way to go.

Also this seems to be arguing from a limiting idea about what macros do. Macros are not always the right solution for any problem, in fact often they are not, but there are things you simply cannot do otherwise (without syntactic clutter), like for example changing the order of evaluation.

I also don't agree with your point about only making toy examples easier to write. For example I have written macros for implementing new define forms, which allow to specify contracts for function arguments and return values, or a macro for automatically defining functions that communicate to API routes, based on the function name, which I used to implement a proof of concept docker client for Scheme. Those are not toy examples, but real world applications, where a small macro can have big effect.

If you think macros need to be big and elaborate and complicated and otherwise are toys, then you don't really understand the power of macros. One of my favorite macros is the following threading macro:

    (define-syntax ->
      (syntax-rules ()
        [(-> expr) expr]
        [(-> expr* ... (op args* ...))
         (op args* ... (-> expr* ...))]
        [(-> expr* ... op)
         (op (-> expr* ...))]))
Small, but a great addition to the code, that improves readability in many places of the code. It doesn't have to be big or long, in order to not be a toy example, but actually be a useful macro.

Re: A road to Lisp: Why Lisp

#266

Earlier quoted context omitted.

You sweet, summer child, just so you know, I have existed for nearly half a century in this world where the larger part of it I have spent dealing in computing. I have seen and dealt with more programming languages (including Ruby) than you can count with your fingers and toes and that number keeps growing still. I'm not advocating for any particular language, runtime, framework or paradigm - do use whatever your hea…

Apparently you're not experienced enough to have used a Lisp that doesn't have a REPL, nor a non-Lisp that does have a good one. You obviously haven't implemented a Lisp either, because then you'd understand what homoiconicity actually gives you. And also how a REPL is implemented (since you wouldn't just get one for free, it's a tool to implement).

You're wrong on every count about my experience. And once again, please: I'm just saying that stages in Read-Eval-Print-Loop does have differences in homoiconic and non-homoiconic languages. THAT'S ALL I SAID. There's zero controversial taste in that statement. None. I'm not bashing on Ruby, or Python, or any language you favor. I'm not telling you to use this or that. I'm just pointing out at specific differences that exist.

Re: A road to Lisp: Why Lisp

#267

Programming is in tension between the Light Side and the Dark Side. The Light Side is about preventing the programmer from making mistakes: Get rid of go-tos! Add static types! Do not allow a bug to be expressible. The Dark Side is about giving power to the programmer: Macros? Obviously. Operator overloading? Self-modifying code? Multi-line reg-exps? Go to town! The Light Side knows programmers are flawed and imposes…

I'll paraphrase someone who commented on HN once (about dynamic vs static typing, IIRC): permissive languages are enablers for solo programmers, but you need more restrictive languages for team programming. This matches my experience because you don't chose who you work with. Skill level is uneven among the team. Less skilled co-workers will make mistakes that will have more consequences when the language is more per…

> Less skilled co-workers will make mistakes that will have more consequences when the language is more permissive.

Even the most skilled engineers are going to make these mistakes sometimes. That's the whole point of more restrictive tooling, and why Rust has exploded.

Don't get me wrong. I love Lisp, and use it in some personal projects. But I wouldn't want to use it in must not fail scenarios unless those situations can wait for a human to fix the running image.

Re: A road to Lisp: Why Lisp

#268
post #258

Personally, I grew beyond this. I tried Common Lisp, Forth and Haskell. I enjoyed books On Lisp and Let Over Lambda. Now I think the best programming language is Barry Jay's Triage Calculus, which is close to combinatory logic or untyped lambda calculus. But unlike lambda calculus (which is easily expressed in TC), triage calculus has a built-in quoting and introspection (similar to Lisp's CAR and CDR), which lets yo…

Do you mean Tree Calculus? https://treecalcul.us

Yes, although I wish he would call it properly triage calculus (see https://treecalcul.us/specification/), because although the languages are almost identical in power, the original tree calculus and triage calculus rules lead to different programs.

Re: A road to Lisp: Why Lisp

#269
post #249

Earlier quoted context omitted.

I don't quite understand what you mean by generic collections in this case. Do you want to restrict a list to only contain one specific type?

I wrote that quickly and should have edited it for clarity. What I meant was a new collection type that could be treated as a sequence. https://www.lispworks.com/documentation/HyperSpec/Body/17_a.... None of the functions operating on sequences are generic, so you cannot create your own type and do a `defmethod` and have them automatically adapt to it. And unlike `print-object` which gets used by format and print and…

Oh, I understand. For what it's worth, some implementations do have that as an extension, although I've personally never used it. See http://www.sbcl.org/manual/#Extensible-Sequences.

Re: A road to Lisp: Why Lisp

#270

[dead]

The inflection of the copula "can be" is exactly the problem, as is the 'anything computable'. There's a reason Isabelle is used as a proof assistant, and Prolog isn't. The former guarantees sound, mathematical rigor that can't be trivially violated. The latter doesn't. Prolog is much more powerful and flexible than Isabelle. Power and flexibility are disastrous for safety. As someone else said elsewhere in this thread, safety is about restrictions.

In this case, it actually runs deeper than this, there are structural issues which prevent them from being sound right from the beginning. It's the mere fact that the whole behavior of your DSL can be silently changed without touching the module it's defined in, or the file it's being imported into. A well-meaning junior, in a completely different department, can completely destroy your invariants in a completely unrelated part of the codebase. Whether by shadowing runtime functions, or a package-level collision that exists upstream. Even Scheme's hygienic macros don't actually solve this, they just make it less likely. At the point in which you have no behavioral guarantees of semantics, you do not have a safety mechanism at all.

> There is no 'evaluative indirection' because a macro is a _compiler_ and the underlying code can be extremely alien to the DSL semantics.

This is a trivial contradiction. That is precisely evaluative indirection. Yes, stacking multiple compilers on top of each other, especially when their semantics are non-isomorphic, is a massive burden of mental overhead. Given you already have this problem with the mapping between Lisp and binaries, adding even more complicated layers on top of it is a recipe for disaster. Particularly because: you are going to make mistakes in your logic. Macros provide no means to stop you from blowing your own leg off when writing them. That is not what they are designed for.

Finally, you cannot escape the awkwardness of the underlying metaprogramming system, you will always have to wrestle with quotation and quasiquotation. You have to define a DSL before you can use it.

Only at the point in which you treat and use Lisp like an unserious toy, rather than the powerful tool that it is, can the fantasy of "macros are a safety mechanism" begin to make sense.

Post reply on HN