[HN Gopher] LSP client in Clojure in 200 lines of code
___________________________________________________________________
LSP client in Clojure in 200 lines of code
Author : vlaaad
Score : 159 points
Date : 2025-05-11 17:38 UTC (1 days ago)
(HTM) web link (vlaaad.github.io)
(TXT) w3m dump (vlaaad.github.io)
| whalesalad wrote:
| This is the most Java-y Clojure I've probably ever read. Just use
| Java? It's so verbose and complex for what it is doing. Breaking
| this down into smaller functions and using core.async would make
| it even more succinct.
|
| Just want to emphasize this because clojure is indeed a small,
| lesser known language that has a hard enough time attracting
| users. This is not what anyone would consider an idiomatic
| example of using clojure.
| roenxi wrote:
| Would it be 200 lines of Java? It'd be 200 lines of just for
| the boilerplate. It isn't really a selling point of Clojure
| because it is subjective, but low-syntax high-terseness look of
| the code is in itself a reward for using the language.
|
| And there isn't anything especially wrong with sticking to Java
| primitives if someone is comfortable with them. They work fine
| for Java programmers. The dude doesn't need to learn a new
| async library to write an LSP client if he doesn't feel like
| it. Code works, its easy to read, easy to understand and
| modify.
| koito17 wrote:
| Line count is not very useful to compare without the context
| of standard library size, third-party dependencies, etc. The
| code in TFA depends[1] on a JSON library[2] that is about a
| thousand lines of code (excluding tests) wrapping a Java
| library for JSON decoding.
|
| Then there's other things to consider, like the fact that
| this LSP client, while succinct, pays not only the cost of
| loading Jackson, but also the cost of loading clojure.core,
| which is quite non-trivial[3]. Startup time for LSP servers
| and clients definitely matters to some, considering that e.g.
| even clojure-lsp recommends running native executables over
| JAR files[4]. Can't find documentation proving it's for quick
| startup, but it's a plausible rationale for their
| recommendation of a binary over a JAR.
|
| Note: I have used Clojure professionally and in hobby
| projects. I think it's nice that one can interactively
| develop a minimal LSP client and the resulting amount of work
| is roughly 200 lines of code. I say "minimal" because it's
| unclear how this client deals with offsets reported by LSP
| servers, which are all given as offsets in a UTF-16 encoded
| string. In any case, I still think advertising "LSP client in
| 200 lines of code" hides valuable information regarding
| functionality, implementation, "actual" code size, and trade-
| offs made in the choice of technology stack.
|
| [1] https://github.com/vlaaad/lsp-clj-
| client/blob/a567e66/deps.e...
|
| [2] https://github.com/metosin/jsonista/blob/c8f2b62/project.
| clj...
|
| [3] https://clojure-goes-fast.com/blog/clojures-slow-
| start/#cloj...
|
| [4] https://clojure-lsp.io/installation/#embedded-jar-legacy-
| exe...
| pron wrote:
| Between records and compact classes [1] Java's boilerplate
| isn't what it once was.
|
| [1]: https://openjdk.org/jeps/512
| newlisp wrote:
| It's idiomatic "low-level" Clojure, though. Not everything is a
| happy place where you're just manipulating maps and vectors
| like in most examples.
| 0x1ceb00da wrote:
| > lesser known language that has a hard enough time attracting
| users
|
| For very good reasons.
| dig1 wrote:
| I don't see why this wouldn't be considered idiomatic clojure
| code; it makes proper use of all the facilities provided by the
| language and the main intention of this code is to follow the
| article. Additionally, the clojure core team often encourages
| not to shy away from using java code directly, as this approach
| strikes a good balance between performance and language
| expressivity.
|
| > It's so verbose and complex for what it is doing. ... and
| using core.async
|
| I think this code is actually quite straightforward and easy
| for a clojure developer to understand. In fact, using
| core.async in this case would be overkill and could complicate
| things further.
| daveliepmann wrote:
| This looks like the other completely normal, idiomatic Clojure
| programs I've seen which manipulate a StringBuilder. And as
| Clojurians go I'm far to the succinctness/concision-preferring
| end of the spectrum.
|
| I'm curious to see your core.async-based version :)
| askonomm wrote:
| Holy crap is this unreadable or what (notably the lsp-base fn).
| There's a reason why in most Clojure companies I've worked at we
| try to make as small functions as possible, because otherwise it
| very very quickly becomes an unreadable mess, and you write code
| after all for humans to read, because if you didn't, you might as
| well just write binary. But, I'm not surprised many people don't
| want to get into Clojure or Lisps in general, because it takes a
| boatload of conventions and active discipline to make it a good
| experience.
| slifin wrote:
| To me something unreadable is code that I cannot statically
| make any assertions about the runtime behaviour of the code
|
| This function you're complaining about looks like 2 virtual
| threads doing program input reading and output writing for the
| LSP client given some ArrayBlockingQueues in about 25-30 lines
|
| If I wanted the complete story I could use Clojure's inbuilt
| test runner to slip some ArrayBlockingQueues in there and run
| it under record with Flowstorm
|
| Then leisurely seek through the entire state of the program, to
| get the play-by-play of how this works
|
| There are so many good design choices in this language and a
| good 30% of colleagues I run into are not even doing the basics
| of like running a REPL, I think some people just need to clock
| in with a decade of C# or PHP or TS or JS or Python or whatever
| to get a taste of a language with next to no inbuilt
| immutability, statements instead of expressions, no reload-
| ability in the language semantics and just crapshot debuggers
| that run in lockstep with the program execution
___________________________________________________________________
(page generated 2025-05-12 23:02 UTC)