Earlier quoted context omitted.
Part of the visibility issue is that there are very few big-name open source Common Lisp success stories which aren't CL implementations, or CL ecosystem tools. Most of the things in that list are big-ticket enterprisey applications.
There are a lot of CL open source tools and applications. But they tend to be specialized to domains which are exotic to many people. Math: Maxima and Axiom Blackboards: GBB Robots: ROS Music: OpenMusic Bioinformatics: Biobike Theorem Provers: ACL2, PVS AI / Logic: Racer, KM etc etc...
Why Lisp?
221–230 of 248 posts
Re: Why Lisp?
#222Earlier quoted context omitted.
A higher order function doesn't serve the same purpose as a macro. A higher order function is meant to be applied, called, composed etc. A lisp macro is a different type of abstraction. For example, many people think that macros are just hiding lambda's of higher order functions. This is wrong. A macro abstracts over implementation details of a construct to make it read naturally. For example, you can write a functio…
Your point seems to be that macros allow for a slightly more natural syntax for certain things, but I can do pretty much the same thing in a language with a natural HOF syntax (Ruby): with_open_file filename do |f| do stuff with file end And for your second example: # our 'macro' function def defclass(name, parent, &blk) k = Class.new(parent) k.instance_eval(&blk) Kernel.const_set(name, k) end defclass :Tiger, Animal…
Re: Why Lisp?
#223The article doesn't discuss macros, which is one of the answers to "Why Lisp?" I didn't "get" macros until I read a footnote in the (freely available) book Practical Common Lisp . In chapter 7, it introduces the `dolist` macro. DOLIST loops across the items of a list, executing the loop body with a variable holding the successive items of the list. This is the basic skeleton (leaving out some of the more esoteric opt…
On the other hand, this really hurts readability. When reading other people's code you now effectively have to learn what "language" they use too. I'd say it's probably worth that cost, if used judiciously.
Paul Graham addressed this in On Lisp (page 59) [0]:
>So yes, reading a bottom-up program requires one to understand all the new >operators defined by the author. But this will nearly always be less work than >having to understand all the code that would have been required without them. >If people complain that using utilities makes your code hard to read, they >probably don’t realize what the code would look like if you hadn’t used them.
>Bottom-up programming makes what would otherwise be a large program look >like a small, simple one. This can give the impression that the program doesn’t >do much, and should therefore be easy to read. When inexperienced readers look >closer and find that this isn’t so, they react with dismay.
>We find the same phenomenon in other fields: a well-designed machine may >have fewer parts, and yet look more complicated, because it is packed into a >smaller space. Bottom-up programs are conceptually denser. It may take an effort >to read them, but not as much as it would take if they hadn’t been written that way.
Re: Why Lisp?
#224Earlier quoted context omitted.
There are a lot of CL open source tools and applications. But they tend to be specialized to domains which are exotic to many people. Math: Maxima and Axiom Blackboards: GBB Robots: ROS Music: OpenMusic Bioinformatics: Biobike Theorem Provers: ACL2, PVS AI / Logic: Racer, KM etc etc...
Exactly. Where's CL's Rails, or Eclipse, or Postgres?
Re: Why Lisp?
#225Earlier quoted context omitted.
Aren't macros breaking the homocionicity of the lisps? And making maintenance more difficult (aren't you re-inventing a new language with macros?)? Where does the "First rule of the macro club" coming from? When should you break it?
>maintenance If used right, macros make things much more maintainable. If you have a hundred nearly-identical codeblocks, where only (say) a string constant is varying, and suddenly you need to change what those blocks do, bam, you've got a hundred blocks to change. If those blocks had been refactored with a macro, all you have to do is change the macro once. It also prevents the new guy from coming and doing a banda…
Re: Why Lisp?
#226The article doesn't discuss macros, which is one of the answers to "Why Lisp?" I didn't "get" macros until I read a footnote in the (freely available) book Practical Common Lisp . In chapter 7, it introduces the `dolist` macro. DOLIST loops across the items of a list, executing the loop body with a variable holding the successive items of the list. This is the basic skeleton (leaving out some of the more esoteric opt…
Re: Why Lisp?
#227Earlier quoted context omitted.
Aren't macros breaking the homocionicity of the lisps? And making maintenance more difficult (aren't you re-inventing a new language with macros?)? Where does the "First rule of the macro club" coming from? When should you break it?
>Aren't macros breaking the homocionicity of the lisps? No. How would macros break homoiconicity? They expand into atoms and lists (and other datatypes), the same stuff of which macro-free programs are made. >And making maintenance more difficult? No. Unless you intentionally write unmaintainable macros. It's the same as if you write unmaintainable functions, classes, etc. They're just abstracting a different thing—…
You should break that rule only when the behavior can’t be written as a function, such as these cases:
• The call needs to avoid evaluating its arguments. For example, the `if` built into the language is sometimes defined as a macro.
(if (= (+ 2 2) 4)
(print "math works")
(print "math is broken"))
If `if` were a function, it would first print both statements, and then return the return value of `print` in whichever branch. By making `if` a macro, it can avoid evaluating the branch that is inapplicable.• The call relies on information about the environment only available at compile-time. Perhaps the macro reads a configuration file on the developer’s computer to decide how to set something up.
• You have profiled the program and determined that it is better to run the function at compile-time. For example, you might want to make `(regex "[a-z][a-z0-9]+")` compile the string to a regular expression at compile-time instead of run-time.
Re: Why Lisp?
#228Earlier quoted context omitted.
>maintenance If used right, macros make things much more maintainable. If you have a hundred nearly-identical codeblocks, where only (say) a string constant is varying, and suddenly you need to change what those blocks do, bam, you've got a hundred blocks to change. If those blocks had been refactored with a macro, all you have to do is change the macro once. It also prevents the new guy from coming and doing a banda…
Not a lisper, so bear with me please. Couldn't you do this with a function?
I listed some situations where you need a macro in my comment that explains the “first rule of the macro club” you asked about: https://news.ycombinator.com/edit?id=9513988
Re: Why Lisp?
#229(I have a subtle optimization for S-expression syntax)(I am surprised nobody ever thought of it)(When S-expressions are in a sequence use an extra (special) delimiter plus the regular token separator to separate expressions)(Maybe use dot? (period I think some call it)) Like so. I think it could catch on. And you get rid of so many round bracket block delimiters (at least for S-expressions on the same level. for nest…
(define-record-type omap-entry
(make-omap-entry key item next prev)
omap-entry?
(key omap-entry-key set-omap-entry-key!)
(item omap-entry-item set-omap-entry-item!)
(next omap-entry-next set-omap-entry-next!)
(prev omap-entry-prev set-omap-entry-prev!))
would be written like this? (define-record-type omap-entry
make-omap-entry key item next prev.
omap-entry?,
key omap-entry-key set-omap-entry-key!.
item omap-entry-item set-omap-entry-item!.
next omap-entry-next set-omap-entry-next!.
prev omap-entry-prev set-omap-entry-prev!)
(To mark `omap-entry?` as being a value, not the S-expression `(omap-entry?)`, I decided to place a comma after it instead of a period. The alternative is requiring no punctuation and making newlines significant.)Well, I suppose it does look better.
However, the idea is of limited applicability. While looking through the example project (https://github.com/axch/test-manager), I had trouble finding some code where this would actually be useful – most code has too much nesting.
That’s why I prefer another solution for removing excess parens from Lisp syntax: making whitespace significant. It improves the syntax in cases where your periods would help, and it applies in additional cases as well.
“Sweet-expressions” (http://readable.sourceforge.net/) is an implementation of that. Here is the above code ran through the `sweeten` tool to convert it to sweet-expressions:
define-record-type
omap-entry
make-omap-entry key item next prev
omap-entry?
key omap-entry-key set-omap-entry-key!
item omap-entry-item set-omap-entry-item!
next omap-entry-next set-omap-entry-next!
prev omap-entry-prev set-omap-entry-prev!
Absolutely no parentheses necessary, while still preserving homoiconicity. You don’t even have to remember the `)` at the end of the last nested line. And I chose this example code to look good with your idea – when the code has more nesting, sweet-expressions look even better.About the interrogative mood `?` you describe: it might simplify `if` statements. But that would require removing the convention where boolean-returning functions have a name ending in `?`, such as `omap-entry?` in the example. It’s a tradeoff.
Re: Why Lisp?
#230Earlier quoted context omitted.
One that helped some Java friends understand is passing blocks of code, but still having it look like just writing code. Imagine instead of try/catch/finally, a transaction/commit/rollback in Java: transaction { // everything in here is in one transaction } commit { // do stuff if the commit is successful } rollback { // do stuff if we rollback } All the try's and catch's can be stuff into the macro. It can be made t…
I find this argument pretty unconvincing too. >For all practical purposes, you can't add that to Java. You'll always have to wrap up your transactions in boilerplate. Aren't you wrapping the lisp code in boilerplate when doing the macro too? This appears to be the same as your other example. Java has lambda expressions (since Java 8) that could do this. If you are wrapping it in a macro, how is it different than wrap…
Here's the version with macros:
(transaction (do-stuff)
(do-stuff-if-commit-successful)
(do-stuff-if-rollback))
Here's the version without macros: (transaction (lambda () (do-stuff))
(lambda () (do-stuff-if-commit-successful))
(lambda () (do-stuff-if-rollback)))
Which do you find more readable? Notice how there's no boilerplate in the macro version.> This appears to be the same as your other example. Java has lambda expressions (since Java 8) that could do this.
What are the odds that Java 8 has provided everything you could want in out of Java? Macros let you add things in a better way than you could otherwise get. Look back to my prior example of Java 5's expanded for. You see how useful that was? If Java had had macros, people wouldn't have had to suffer through nine years without the improved for loop.