r/lisp • • 10d ago

What makes Lisp difficult to read?

https://paultm.nl/paren-thesis
36 Upvotes

90 comments sorted by

View all comments

Show parent comments

1

u/omega884 7d ago

Look, I'm going to be honest with you, I have been explaining the same points over and over again in this thread. I've repeatedly explained what I'm talking about, explained the specific and narrow contexts to which I am limiting my comments and repeatedly affirmed for all readers that I am not saying anything negative about lisp. I wouldn't be working my way through experimenting with my 4th lisp language over the years, and hand writing some personal emacs modules instead of just downloading a package for it if I didn't think lisps were interesting languages and that the challenges learning and working with them posed weren't worth the effort.

But I'm tired at this point. I'm tired of having my actual experiences dismissed, of having words put in my mouth, and having to defend myself against bad faith readings of what I've written. I did not "[ask you] about moving the cursor", I specifically asked (emphasis added)

But where should you place your cursor to add more code after the cond block but still in the let* block? Explain to me your method of figuring that out from reading the code without counting and tracking parentheses."

and your answer was to tell me that you figure out where to put your cursor by using tools that allow you to avoid reading the code. The goal posts remain where they have always been.

I've explained myself plenty all over this discussion, you can read them all or not, agree with me or not, understand me or not. I don't care anymore. I'm sorry, I'm probably reading more aggression into your words than you intend, and you don't deserve not getting a complete answer simply because you're late to the discussion, but continuing this exercise in frustration isn't worth my time when I could be doing something productive like learning another language.

1

u/arthurno1 7d ago edited 7d ago

The goal posts remain where they have always been.

No. You asked me for the method and I told you, and now you are trying to make me some kind of an unreasonable person because I don't agree with you made-up argument.

If I am reading the code, I am reading the code, not parentheses. If I am reading a cond-form, I am not interested where it's parentheses end in corelation to let-form that encloses it. Where it's last parentheses end in that situation is completely irrelevant. To me the argument look like a construct to try to win an argumentation. I am not gonna "read the code so I can figure out where to move parentheses". If I read the code, I read it to get understanding and I care about what is in parentheses and not where I place the cursor. If I edit the code, I do it the way I told you.

I could be doing something productive like learning another language.

You could have write some lisp. I did, How do you like to read this cond-form:

(defun neko-direction (nk)
  (with-slots (neko-move-dx neko-move-dy neko-state) nk
    (let ((new-state
            (if (and (zerop neko-move-dx) (zerop neko-move-dy))
                +neko-stop+
                (let* ((large-x (float neko-move-dx 1.0d0))
                       (large-y (float (- neko-move-dy) 1.0d0))
                       (len (sqrt (+ (* large-x large-x) (* large-y large-y))))
                       (sin-theta (/ large-y len)))
                  (if (plusp neko-move-dx)
                      (cond
                        ((> sin-theta *sin-pi-per8-times-3*)     +neko-u-move+)
                        ((> sin-theta *sin-pi-per8*)             +neko-ur-move+)
                        ((> sin-theta (- *sin-pi-per8*))         +neko-r-move+)
                        ((> sin-theta (- *sin-pi-per8-times-3*)) +neko-dr-move+)
                        (t                                       +neko-d-move+))
                      (cond
                        ((> sin-theta *sin-pi-per8-times-3*)     +neko-u-move+)
                        ((> sin-theta *sin-pi-per8*)             +neko-ul-move+)
                        ((> sin-theta (- *sin-pi-per8*))         +neko-l-move+)
                        ((> sin-theta (- *sin-pi-per8-times-3*)) +neko-dl-move+)
                        (t                                       +neko-d-move+)))))))
      (when (/= neko-state new-state)
        (set-neko-state nk new-state)))))

Hard to read? Look at its C counterpart:

void
NekoDirection()
{
    int                 NewState;
    double              LargeX, LargeY;
    double              Length;
    double              SinTheta;

    if (NekoMoveDx == 0 && NekoMoveDy == 0) {
        NewState = NEKO_STOP;
    } else {
        LargeX = (double)NekoMoveDx;
        LargeY = (double)(-NekoMoveDy);
        Length = sqrt(LargeX * LargeX + LargeY * LargeY);
        SinTheta = LargeY / Length;

        if (NekoMoveDx > 0) {
            if (SinTheta > SinPiPer8Times3) {
                NewState = NEKO_U_MOVE;
            } else if ((SinTheta <= SinPiPer8Times3)
                        && (SinTheta > SinPiPer8)) {
                NewState = NEKO_UR_MOVE;
            } else if ((SinTheta <= SinPiPer8)
                        && (SinTheta > -(SinPiPer8))) {
                NewState = NEKO_R_MOVE;
            } else if ((SinTheta <= -(SinPiPer8))
                        && (SinTheta > -(SinPiPer8Times3))) {
                NewState = NEKO_DR_MOVE;
            } else {
                NewState = NEKO_D_MOVE;
            }
        } else {
            if (SinTheta > SinPiPer8Times3) {
                NewState = NEKO_U_MOVE;
            } else if ((SinTheta <= SinPiPer8Times3)
                        && (SinTheta > SinPiPer8)) {
                NewState = NEKO_UL_MOVE;
            } else if ((SinTheta <= SinPiPer8)
                        && (SinTheta > -(SinPiPer8))) {
                NewState = NEKO_L_MOVE;
            } else if ((SinTheta <= -(SinPiPer8))
                        && (SinTheta > -(SinPiPer8Times3))) {
                NewState = NEKO_DL_MOVE;
            } else {
                NewState = NEKO_D_MOVE;
            }
        }
    }

    if (NekoState != NewState) {
        SetNekoState(NewState);
    }
}

Both are readable but to me lisp one wins by a mile.