Table of Contents
Why Lisp?
A lot of my friends ask me why in the hell I use Lisp. They can't comprehend it. Most of them seem to be turned off by the parenthesis, actually, and at least one of them I'm certain is just complaining to be difficult, and has never actually given lisp any more than a moment's worth of thought (terrible excuse for a logical person he is!)
I use Lisp for my initial prototypes and ideas, and in general for things that don't need strong correctness guarantees or extreme speed.
Speed
First, it's important to note that computers get much faster year over year. For personal projects (and those with a reasonable number of users (ie. any small business), a single computer is now most probably powerful enough to serve as your entire infrastructure without serious problems. We now use multiple systems for redundancy and failover, not for performance reasons (at small business/team scales).
Thus, interpreted languages are plenty fast enough. Don't forget that for some ungodly reason the industry seems to like using node.js for backend. Lisp interpreters (SBCL especially) actually compile down to machine code, you can disassemble it and it's usually very similar to C code, look:
(defun fibonacci-unoptimized (n) (if (member n '( 0 1)) 1 (+ (fibonacci-unoptimized (- n 2)) (fibonacci-unoptimized (- n 1))))) (print (disassemble #'fibonacci-unoptimized))
; disassembly for FIBONACCI-UNOPTIMIZED ; Size: 143 bytes. Origin: #x539BB1B4 ; FIBONACCI-UNOPTIMIZED ; 1B4: 498B4510 MOV RAX, [R13+16] ; thread.binding-stack-pointer ; 1B8: 488945F0 MOV [RBP-16], RAX ; 1BC: 48837DE800 CMP QWORD PTR [RBP-24], 0 ; 1C1: 7508 JNE L2 ; 1C3: L0: BA02000000 MOV EDX, 2 ; 1C8: L1: C9 LEAVE ; 1C9: F8 CLC ; 1CA: C3 RET ; 1CB: L2: 48837DE802 CMP QWORD PTR [RBP-24], 2 ; 1D0: 74F1 JEQ L0 ; 1D2: 488B55E8 MOV RDX, [RBP-24] ; 1D6: BF04000000 MOV EDI, 4 ; 1DB: FF142550060050 CALL [#x50000650] ; #x52A00F80: GENERIC-- ; 1E2: 4883EC10 SUB RSP, 16 ; 1E6: B902000000 MOV ECX, 2 ; 1EB: 48892C24 MOV [RSP], RBP ; 1EF: 488BEC MOV RBP, RSP ; 1F2: B842CA3450 MOV EAX, #x5034CA42 ; #<FDEFN FIBONACCI-UNOPTIMIZED> ; 1F7: FFD0 CALL #S(SB-X86-64-ASM::REG :ID 0) ; 1F9: 480F42E3 CMOVB RSP, RBX ; 1FD: 488955E0 MOV [RBP-32], RDX ; 201: 488B55E8 MOV RDX, [RBP-24] ; 205: BF02000000 MOV EDI, 2 ; 20A: FF142550060050 CALL [#x50000650] ; #x52A00F80: GENERIC-- ; 211: 4883EC10 SUB RSP, 16 ; 215: B902000000 MOV ECX, 2 ; 21A: 48892C24 MOV [RSP], RBP ; 21E: 488BEC MOV RBP, RSP ; 221: B842CA3450 MOV EAX, #x5034CA42 ; #<FDEFN FIBONACCI-UNOPTIMIZED> ; 226: FFD0 CALL #S(SB-X86-64-ASM::REG :ID 0) ; 228: 480F42E3 CMOVB RSP, RBX ; 22C: 488BFA MOV RDI, RDX ; 22F: 488B55E0 MOV RDX, [RBP-32] ; 233: FF142548060050 CALL [#x50000648] ; #x52A00F10: GENERIC-+ ; 23A: EB8C JMP L1 ; 23C: CC10 INT3 16 ; Invalid argument count trap ; 23E: CC14 INT3 20 ; UNDEFINED-FUN-ERROR ; 240: 00 BYTE #X00 ; RAX(d) ; 241: CC10 INT3 16 ; Invalid argument count trap NIL
I also want to point out that, as anyone who's done leetcode (urgh! That deserves its own post!) knows, speed is actually more about the cleverness of your algorithm, not about your language (with the exception of special case languages like brainfuck or unlambda!)
Lisp is such an elegant, powerful, expressive language that you're able to use the more powerful and elegant abstract trickery to write the faster algorithms much more easily, so in general your code should usually be a lot faster than the fast-but-infantile code a C developer writes, unless that C developer has a month to optimie the code thoroughly. (C++ developers will need much less, since that's quite a powerful language now!)
Environment
Emacs and SLIME with paredit and so on. Note: My emacs config.
Enough has been said on the internet about Emacs and its integration with lisp. It is, simply, the single most powerful programming environment known to man (for lisp), and that remains undisputed.
Macros
People who code in languages without real macros will not be able to comprehend them, let alone begin to understand their glory. They simply don't have the mental framework needed. It would be like explaining to a geometer that there exists a connection between geometry and algebra, and if they could only study it a little, they'd get it.
It sufficies to say that all code is similar, there are only a few ideas that you reuse. Wherever there is redundancy in the code, and I really do mean wherever, you can write code to generate that code and eliminate it. That code-generating code is itself generatable, to the point where an obsessive lisper can simplify the codebase to a horrible mega-abstracted structure which probably is glorious and powerful enough to become its own branch of mathematics. (In fact, this is how new branches of mathematics are made! Abstracting repeatedly and distilling out common compoments until..)