I recommend Clojure/ClojureScript. It's quite practical for web development and also an excellent gateway into functional programming.
Here is one anecdata. When my daughter was 14 I taught her a Lisp and then a traditional programming language. She found the random syntax of the trad formatted language bizarre.
Two things were important to make it "click" for me:
1) I realized that "Lisp has many parentheses" actually has it backwards. A Lisp code has about as many parentheses as an equivalent Java code; there is almost a 1:1 relation. It just has less of... unnecessary things. Which results in more "parentheses per square meter", but from this perspective that is a good thing!
2) Some people complain that dense code is more difficult to read. But you don't have to turn everything into one-liners! You can format it into just as many lines as the Java code would have, except the lines will be much shorter now, and therefore easier to read. In Java, you don't have much freedom with formatting, because a typical statement takes more that half of the screen width, so you are stuck with one long column. Lisp with shorter statements gives you more freedom. You can abuse it to write hard-to-read code. You can also use it to design beautiful code that is easy to read. Also, there is a huge difference in legibility of a code that fits in one screen (so you can see it whole at once) and one that does not; and the Lisp functions are shorter on average.
function doSomething(a, b) {/* something! */}
is okay, since the “function” keyword is a simple indicator, and it’s clear that it’s a function declaration. The kids these days often use const doSomething = (a, b) => {/* whatever */}
and that is awful for readability. It’s much harder to scan for function definitions amidst value declarations. I’m nearly 20 chars in before I even know it’s a function. Worse yet is something like const doSomething = (a, b) => b => “some closure”;
This threatens to stretch my capability to understand the context I’m in when reading it. “b” is just kinda floating there amongst the infixed arrows, and I have to read on to know it’s an argument, and I have to run through the calculation of what “b” is every time. Compare: (defn do-something [a b] (fn [b] “some clojure”))
I can count the parens, there is no implicit scoping of the function body. My editor can too, which means I often don’t have to. It’s obvious that it returns a function. This part is Clojure-specific, but I also have strong guarantees that the returned function cannot pull the rug out from under me with whatever I pass to it by arbitrarily mutating some argument.To me at least, (fn-name args) was both more readable and makes more sense than fnName(args) after a few days, and I never learned a lisp until I was 37. Maybe I just found a style preference later in life, marking me as a lisp survivor. Maybe we’ve just been doing it wrong for decades and have grown accustomed to it. I can’t say. But the power of the language itself coupled with the code editing features it enables makes me think that the whole field has been on the wrong track, or maybe even off the rails, for decades.
The JS code I write now is more flexible, more resilient, more testable, and more maintainable as a result of learning Clojure. I doubt anyone can say the opposite: that a C-style language improved their understanding of lisp. (Not addressing you with this part, dear poster, since your gripe is rooted in the syntax.)
[NaN] “kids these days“ is just friendly ribbing. I’ve got the grey beard now, so I‘m free to play the part. Language maintainers tend to be older, and we cause more problems.