Super expressive and readable. I went from not being able to read clojure to now having written a few apps in it and feeling comfortable with the syntax in a few weeks.
I definitely write less code to achieve the same ends but it’s still early days for my lisp adventure.
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 machine but I don't believe it's optimal readability for humans. Other disadvantages include lack of typing & poor tooling support. There's certainly domains it excels at due to its intrinsic qualities but I don't see it ever becoming a popular mainstream general purpose programming language.
After one year of clojure, it's annoying for me to read classic C-like languages. There's also people who prefer reading code without syntax color, so clearly this is a subjective matter.
I find clojure tooling really good, and it's hard to go back to a non-repl oriented workflow (this is not at all the same as something like python or javascript repl). But this is admittedly a gray area, there's areas where it's immature compared to other languages, and others where it shines because of it's repl-oriented nature.
The lack of typing is certainly a sound argument, although my personal opinion is that for many use cases it isn't required. This is going to be a contentious opinion, I'm sure.
Note that clojure has an advantage on this over other dynamic languages: idiomatic clojure code means that pretty much everything is pure data, in basic structures: maps and vectors. So the functions that you use to operate on your data are always the same. Over time you'll learn them, and they won't change between domains nearly as much as, say, if you were coding in OOP oriented python.
So when it came time to implementing a .NET LISP [2], I adopted much of Clojure's additional syntax for improved readability & interop with .NET APIs [3]. But you can only improve LISP's syntax so far, e.g. its Template libraries for HTML generation [4] make for horrible HTML DSL's which looks nothing like the HTML it's supposed to generate. The solution to overcome this was basically to not to use LISP for templates, instead create a multi-language scripting language [5] that embeds lisp into it allowing it to Combine strength's of all languages [6], e.g. use LISP for algorithms and Handlebars / JS Expressions for templating.
The REPL is definitely one of its super powers which is one areas where it shines & basically the primary use-case where I still use it. I've created a live "watch" mode & deep integration with .NET libs that I use for discovery, e.g. run DB queries, call HTTP APIs, execute shell scripts, etc. [7]. It especially shines for being able to open a REPL session with a remote production .NET instance letting me inspect its live running state & invoke system functionality like querying its configured RDBMS, executing redis commands, send tweets, emails, etc [8], I've even got it to Live Script Unity objects in-game :) [9], which speaks to the power & elegance of LISP that's able to achieve so much with so little code.
At the same time I don't think REPL-based programming is all that useful during normal development, you can execute encapsulated code fragments fine, but most of the time I'll need my whole environment constructed before being able to inspect it as I would when debugging, so I find it useful for opening a REPL session into a live running instance, but not using the REPL to construct the live instance. So for my dev workflow, static analysis & typing, great IDE, tooling + debugging is a lot more useful.
[1] https://github.com/mythz/clojure-linq-examples
[2] https://sharpscript.net/lisp/
[3] https://sharpscript.net/lisp/#net-interop
[4] https://www.cliki.net/HTML%20template
[5] https://sharpscript.net/docs/syntax#language-block-modifiers
[6] https://sharpscript.net/docs/syntax#combine-strengths-of-all...
[7] https://sharpscript.net/lisp/#run-and-watch-lisp-scripts
[8] https://sharpscript.net/lisp/#techstacks-tcp-lisp-repl-demo
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.
Now I learned C-like language before I learned Lisp-like languages, so that might be the reason I felt I understood Lisp way faster than I felt I understand C-like languages. There is simply less to learn about the language and more to learn about conventions, while C-like languages always have bunch of extra syntax you have to learn, otherwise the compiler cries.
> Other disadvantages include lack of typing & poor tooling support
Not sure these are inherent to Lisps. There are plenty of typed lisps, and even the ones that don't have types, can have types added to them after the fact, just because they are Lisps.
Regarding tooling, I'd say that the entire C-like ecosystem is far behind any Lisp language regarding tooling. I mainly see people using "println"'s for debugging, and sometimes using a "line debugger", while Lisp developers modify their code at runtime and have a far more advanced debugger, something C-like languages will probably never do as well as Lisp-like languages does it, again because of s-expressions and the code they make you write.
So your argument is that all code is as easy to read as long as you have done the work to get familiar with it? I hope you understand that it is bullshit. Brainfuck isn't easier to understand than python no matter how much time you spend with brainfuck.
My point is that "readability" is composed by many factors, not just one. What is "readable" to some will not be "readable" by others. It depends.
For someone who knows Fortran, learning a Fortran-like language is easy (like C or JS). For someone who knows Common Lisp, Clojure is easy. But for someone who knows Fortran, Clojure is less familiar, hence the code will, on a glance, look less "readable".
The other factor is if something is "simple" by itself.
Most of this view comes from Rich Hickey, who wrote Clojure. He discusses "readability" or "simplicity" rather, in his talk "Simple Made Easy". If you haven't seen it before, do yourself a favor and watch it: https://www.infoq.com/presentations/Simple-Made-Easy/
He'll explain it much better, and with further points, much better than I ever can.
I don't disagree with your overall point, but I actually wonder how true that is. If you spent years working in Brainfuck, and had never touched Python, I suspect it would take a bit of effort before Python was as readable to you as Brainfuck.
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.
None of that is strictly impossible in languages without macros but best case you'd have to wrap everything in a copious amount of lambdas making for very nasty looking code. Worst case some compile time optimizations wouldn't be possible.
Any single macro isn't THE killer macro, but giving all of them up is a big deal. Similarly replicating semantics of a single macro in some other language may be doable but getting many of them to compose would be tough.
Most popular languages tend to get new keywords, operators and other forms of syntax over time implicitly acknowledging limitations of the "user space" syntax. You don't see that as much in LISPs since most such things can be implemented as a macro.
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 brevity and clarity.
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.
Python has decorators. They have a specialized, limited interface. Sometimes they are limiting you. A macro would impose no limit.
And in the same way you should think twice about introducing a new code of annotation to your program, you should think twice about adding a macro to a Lisp program.
But boy, when you really need it, it can really be a life saver.
Smaller stuff like hand rolled parsers and serializers are much easier to get working fast (both time to write and time to run) using macros. Since a good chunk of my work is data flow in one form or another, I miss LISP macros a lot.
But if you want to really appreciate macros, you'll need to read some books. There's a lot to learn.
My introduction to lisp was ANSI Common Lisp and On Lisp by Graham. On Lisp is all about macros. You need ACL to understand On Lisp. What you learn is pretty impressive -- I seem to recall one of the later chapters of On Lisp features a compiler for Prolog in two and a half pages of code -- but it requires a certain amount of supporting material. Still, if you want the most direct route to understanding (some of) lisp's greatness, these two would be it.
Another possibility is Paradigms of Artificial Intelligence Programming by Norvig. This one teaches you Common Lisp in the introduction, but I'm not sure it's enough by itself for you to really understand some of the later chapters. If you're prepared though (read ANSI Common Lisp first), this book is a gem. It's less about AI than about transforming and optimizing programs. So, code as data.
You might also look at Practical Common Lisp by Siebel. I haven't read this, but a lot of people liked it, and the code is very real-world (a little dated now, though). It's available online here: http://www.gigamonkeys.com/book/
In general, you wind up learning a couple of different lisps. Common Lisp and Scheme have the best literature, but the lisp that's most in use today is Clojure. Clojure's macro system is a refinement of Common Lisp's. For learning clojure there are a lot of teach-yourself-X-in-21-days type books. The best of them is the O'Reilly book: https://www.oreilly.com/library/view/clojure-programming/978...
Scheme doesn't (always) have macros, but I'd be remiss if I didn't suggest something. The Schemer books are some of the most effective pedagogy I have seen on any subject: The Little Schemer, The Seasoned Schemer and The Reasoned Schemer. They are very cute, but don't let that fool you. They get hard (in TLS's case maybe too hard) at the end.
Finally, there is one of the most important CS books of all time: The Structure and Interpretation of Computer Programs (SICP). It will change the way you think about programming forever. It also explains some important details about how lisp works, and it's so definitive that a lot of them aren't covered elsewhere. ("SICP already did that...") I reread my copy every five years or so, and I always come away knowing something new.
You don't need to read all this stuff to be a good lisp programmer. One or two of these would probably be enough. But I think it's important to have choices. If you want to talk in more depth, my email's in my profile.
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).
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?