1. it violates normal syntax rules you have been practicing since you were 4 years old.
Lisp will say:
(+ (- (\* 3 5) 7) (/ radius pi))
but you are taught since 5:
(3*5 - 7 + radius/pi)
2. Humans are highly adapted to using language, and understanding language constructs such as implicit context rules. Humans reduce token counts and structure in favor of implicit rules and making common patterns shorter. Lisp makes them all explicit, which forces you to cope with way more tokens. Lexical binding was added to Common Lisp almost as an afterthought, and the way LET/LET* force you to add layers of nesting demonstrates that. Every time you assign a variable the 'modern' way, you have to indent another block of code.
A concrete example is introducing local bindings with actions in between. The thought is "calculate this, do something, then continue":
user = find_user(user_id)
require_admin(user)
report = build_report(user)
write_audit_log(user, report)
receipt = send_report(report)
record_delivery(receipt)
In Common Lisp, a direct translation adds a level of nesting for each binding:
(let ((user (find-user user-id)))
(require-admin user)
(let ((report (build-report user)))
(write-audit-log user report)
(let ((receipt (send-report report)))
(record-delivery receipt))))
This is much closer to what is actually happening, and does not require you to understand scoping rules, but it's cumbersome and stupid.
LET* handles consecutive bindings, but here the actions must happen between them. You can use PROGN inside the initializers, or introduce dummy bindings for the actions, but either way you're restructuring a flat sequence to fit the binding syntax.
That's the implicit context I mean: the statement order combined with syntax rules of the language can supply the scope, without requiring a new enclosing expression every time you introduce a local. Lexical scope itself doesn't require this nesting to be explicit in the syntax of the language and it's not helpful for it to be.
3. So many inconsistencies.
Common Lisp uses alternating keys and values for property lists:
'(:name "Ada" :age 37)
Association lists use a list of pairs:
'((:name . "Ada") (:age . 37))
And LET uses two-element binding lists:
(let ((name "Ada")
(age 37))
...)
Lookup conventions differ as well:
(getf plist key)
(gethash key table)
(assoc key alist)
GETF puts the container first; GETHASH and ASSOC put the key first. ASSOC also returns the matching pair, whereas GETF and GETHASH return the value as their primary result.
4. What happens at COMPILE-FILE time is arcane and almost impossible to keep straight.
5. Common Lisp often feels designed primarily to implement Common Lisp, rather than to write useful application code. Its equality predicates are a good example: EQ, EQL, EQUAL, and EQUALP give you four fixed bundles of rules organized around Lisp's own representations.
Want arrays compared by content? EQUALP does that, but also makes string comparisons case-insensitive. Want objects compared by their slots? EQUALP does that for DEFSTRUCT instances, but not ordinary CLOS instances. Want to define what equality means for your class? None of these predicates is a generic function you can extend.
Checking something basic like "when do these two values mean the same thing?", requires a separate operation of your own. None of the equality operators do anything close to what someone might actually want, unless they happen to be implementing CL in which case they are exactly what you want.
Comparison EQ EQL EQUAL EQUALP
Lists containing (1 2) NIL NIL T T
List (1 2) versus (1.0 2.0) NIL NIL NIL T
Strings containing "Ada" NIL NIL T T
String "Ada" versus "ADA" NIL NIL NIL T
Same-type DEFSTRUCT instances, identical slots NIL NIL NIL T
Same-class CLOS instances, identical slots NIL NIL NIL NIL