Live data from Hacker News

Six years of professional Clojure development

falkoriemenschneider.de

161–170 of 209 posts

Re: Six years of professional Clojure development

#161
post #65

Earlier quoted context omitted.

Inheritance from java.lang.Object does not make Clojure data mutable. Blocking for IO does not make Clojure imperative, it not a purely functional language, but its still a functional, not imperative, language. It's possible to build a service in a purely functional language using non blocking IO that also takes minutes to render. No language will save you from a bad design. I do agree that Clojure is not the most su…

The problem is not the built-in Clojure data structures. The problem is that a huge part of Clojure's heavily marketed value proposition is seamless-ish Java interop, which means working with Java objects and all their associated mutability. So while pure Clojure has very controlled mutability, pragmatic Clojure inherits all of Java's problems. And since they hype interop so much, nobody wants to rewrite functionalit…

In practice all the interop lives at the edges of the application. As a concrete example, Pedestal HTTP server has around 18,000 lines of code, and 96% of it is pure functions. All the IO and side effects are encapsulated in the remaining 4% of the code. This has been a common scenario for the vast majority of Clojure programs I've worked on in the past decade.

https://www.youtube.com/watch?v=0if71HOyVjY

Core of the application does data transformations using native Clojure data structures. This is where all your interesting business logic lives. The interop lives at the edges and typically used for stuff like database drivers, HTTP servers, file access, and so on.

Re: Six years of professional Clojure development

#162

Earlier quoted context omitted.

Well, no not really. They started with the conclusion that being a clojure geek is cool and then try to post justify it. Good programming practice isn't a language feature its a programmer feature.

> Good programming practice isn't a language feature its a programmer feature. I'll enjoy reading your production-grade brainfuck code. A language's built-ins and idioms greatly influence what you say and how you say it. If your mother tongue doesn't have words to describe any emotions you'll have a hard time explaining them. You're talking only about the other side of the coin. Yes, people can write terrible clojure…

Do you really think referencing brainfuck, a deliberately difficult language, is the right way to get your point across?

Re: Six years of professional Clojure development

#163

Earlier quoted context omitted.

You're both wrong. If it was dynamicism then why are JS, Python, and Ruby so popular? If it's about being functional then why has Scala got more users? Languages are driven by the platform. There is no Clojure platform that people want to use, so no one uses Clojure. If a language isn't bound to it's own platform, it can share a platform and displace other tools like python, go, and rust do with C and C++ (docker is…

Your hello world actually took one second to start, add libraries for a web application and you will be hitting 2-3 minutes of startup time.

That's actually not correct at all. Hello World is slow because you have to start up the JVM. Once it's started it's rather fast. Your web application has the same JVM startup cost but not a lot more than that.

Re: Six years of professional Clojure development

#164

Earlier quoted context omitted.

I don’t think it’s the parents, but the s-expression themselves. LISPs forces you to maintain the stack for the parse tree in your head, something humans aren’t that great at — s-expressions are the programming language equivalent of center embedding, which is quite alien for human languages (the depth is three at most: compare that to your favorite lisp program)

That's true if you write everything on one line. But how could you fail to notice that most Lisp code is written on multiple lines and indented? The structure is visually laid out, so as not to be maintained in anyone's head.

[deleted]

Re: Six years of professional Clojure development

#165

Earlier quoted context omitted.

I don’t think it’s the parents, but the s-expression themselves. LISPs forces you to maintain the stack for the parse tree in your head, something humans aren’t that great at — s-expressions are the programming language equivalent of center embedding, which is quite alien for human languages (the depth is three at most: compare that to your favorite lisp program)

That's true if you write everything on one line. But how could you fail to notice that most Lisp code is written on multiple lines and indented? The structure is visually laid out, so as not to be maintained in anyone's head.

Line breaks only shorten lines, they do not change the reading direction. Yes, you can start at the bottom and read upwards, but that's unnatural for most.

Compare:

  something.first().second().third()
With:

  (third
    (second
      (first
         (something))))
The end from read must you to understand, and it gets more complicated as your code does. No wonder Clojure's threading macro is so popular, as it would allow you to write it as:

  (-> something first second third)
Fun fact: Lisp was never supposed to be written with S-expressions. They were intermediate representation, for bootstrapping. McCarthy designed M-expression, with function notation, inflix, and sugar'd cond and list; but all that was omitted due to lack of time, and we were left with S-exps.

Re: Six years of professional Clojure development

#166
post #70

Earlier quoted context omitted.

I wrote Clojure for 4 years. I understand your pain points. Dynamic types are an impediment beyond a certain code size.

My team structures projects by breaking things up into small isolated components that can be reasoned about independently. We'll often do it at the level of namespaces, where a namespace will describe a particular workflow or data transformation, and namespaces tend to be 500 lines or less. It's a similar idea to microservice architecture without the overhead of having to actually split the application up into separa…

> We'll often do it at the level of namespaces, where a namespace will describe a particular workflow or data transformation, and namespaces tend to be 500 lines or less

Are there any open source Clojure projects that are structured this way? I'd like to see a working example.

Re: Six years of professional Clojure development

#167
post #87

Earlier quoted context omitted.

Cabal hell is fixed with tooling. Part of that tooling is running tests, but a large part of it is "it compiled together so it should work together" (which is a safety dynamic typed langs dont offer).

No it is not. People still reach out from stack when a dependency has updates and it's not in stack yet. Nobody cares if it compiles together. The point is if a new feature or a bug fix is introduced in a breaking way you cannot bump your dependency and start using it, or progressively move to the new API.

In my experience Stack solved all my cabal hell. I've come to accept some level of dependency juggling, as I had it with every languages.

In Haskell is was more laborious before Stack (the place known as cabal hell) and it got better than average after Stack. Sure, this is just my subjective experience.

> Nobody cares if it compiles together.

Well I didi. If Stack guarantees that a set of software compiles/tests together, then I dont have to do that work.

Re: Six years of professional Clojure development

#168
This is a great article, and I think does a great job of surveying the landscape as it stands. Particularly great to hear reports of ease in hiring and training, which doesn't surprise me, but is nice to be able to cite when risk-averse tech leads express concern about staffing and team-scaling.

I find it somewhat funny that a lot of the critiques here are about purity or types. I really see it as Clojure's core strength that it's both pragmatic and approachable. I'm a math/alegbra nerd, so I love languages like Haskell & OCaml, but they're much more austere for the uninitiated, and have had their own troubles growing. From my experience, the approach of Clojure spec is really nice because it gives you more flexibility/freedom/support in both thinking at a high level about the design of your system (in spec code) before you actually write the (implementation) code itself, while also not requiring it, and letting you add after the fact. You also get a lot for it: generative testing, destructuring, documentation, etc.

That having been said, I'm surprised that more of the pro-type folks here don't seem to be familiar with the [typed clojure](https://github.com/clojure/core.typed) project, which adds optional typing, along the lines of Racket's contract system. To me, it's one of the great testaments to the power of Lisp that you can fairly easily implement types on top of the core language.

My one beef with this article is that scripting with Clojure is now not only possible, but a joy, thanks to GraalVM and [babashka](https://github.com/babashka/babashka)! This has made a HUGE difference in the surface area of problems to which I'm able to apply Clojure.

Re: Six years of professional Clojure development

#169

Earlier quoted context omitted.

That's true if you write everything on one line. But how could you fail to notice that most Lisp code is written on multiple lines and indented? The structure is visually laid out, so as not to be maintained in anyone's head.

Line breaks only shorten lines, they do not change the reading direction. Yes, you can start at the bottom and read upwards, but that's unnatural for most. Compare: something.first().second().third() With: (third (second (first (something)))) The end from read must you to understand, and it gets more complicated as your code does. No wonder Clojure's threading macro is so popular, as it would allow you to write it as…

You have a strawman example of piping via .member() because those () sometimes have arguments; that's what they are there for. Function application has not gone away; it's just combined with obj.member access. It can easily become an unreadable mess that will need some way of splitting across lines and indenting:

  something.first(other.foo(bar.f(x, y)).memb, z).second(x.y()).third(a, b, c)

This:

  (third
    (second
      (first
         (something))))
is just function notation with the location of the opening parenthesis having been re-examined, and commas removed. Function application notation is found in a myriad languages: sin(cos(pow(x, 2))).

With the above indentation, it's very readable to me; it's very clear that calculation starts with (something) and moves in an outward direction.

  (-> something first second third)
Right, yes, so we have threading macros, and people use them. That's not all that goes left to right. Lisp's ancient progn (including implicit progn) goes left to right, as do the arguments of functions and most macros:

  (defun app ()
    (init)
    (event-loop)
    (cleanup)
    (exit 0))
Sequential binding can break up a nested "point-free" expression, as an alternative to threading:

  (let* ((a (first (something)))
         (b (second fi))
         (c (third se))
     ...)

Re: Six years of professional Clojure development

#170

Earlier quoted context omitted.

Curious, because I'm also doing Scheme now, and Clojure was my gateway drug, which Scheme are you using and for what sort of problem? I'm using s7, but that's because my use case is very much oriented to s7's non-typical feature set (it's computer music), but Janet looks really nice too. (Many similarities to both Clojure and s7 actually).

Mostly Chez. Racket is remarkable too -- I have never felt that I was stretching its capabilities. Enjoying Janet, trying to get over Clojure habits where the two differ. Most of my career has been involved in medical diagnostic, data analysis, and desktop applications. Hobby projects include text processing, editors, outliners, wikis, and knowledge management.

Cool, thanks for the details.
Post reply on HN