Live data from Hacker News

Clojure, the Good Parts

rasterize.io

11–20 of 96 posts

Re: Clojure, the Good Parts

#11
post #8

I don't agree on a few points. Global atoms provide a good way to confine state into a single place. In my applications they serve as a in-memory database while everything persistent is written to disk in the form of a log (kafka, file append, you name it). Agents provide a good way to serialise events akin to a transactor. Not every problem requires the parallelism or the concurrency gained by smearing it out into a…

Agents are effectively atoms jammed next to an unbounded queue or unbounded thread pool, depending on whether you use `send` or `send-off`. In cases where you have enough contention to actually need formal serialization, you don't want either of those.

If you have low contention, use an atom. If you have high contention, use atoms and/or pieces of java.util.concurrent.

Re: Clojure, the Good Parts

#12
post #8

I don't agree on a few points. Global atoms provide a good way to confine state into a single place. In my applications they serve as a in-memory database while everything persistent is written to disk in the form of a log (kafka, file append, you name it). Agents provide a good way to serialise events akin to a transactor. Not every problem requires the parallelism or the concurrency gained by smearing it out into a…

Agents are effectively atoms jammed next to an unbounded queue or unbounded thread pool, depending on whether you use `send` or `send-off`. In cases where you have enough contention to actually need formal serialization, you don't want either of those. If you have low contention, use an atom. If you have high contention, use atoms and/or pieces of java.util.concurrent.

Fair points, but they are good to get started and can later be replaced with a proper log e.g. Kafka and a worker thread :)

Re: Clojure, the Good Parts

#13
post #12

Earlier quoted context omitted.

Agents are effectively atoms jammed next to an unbounded queue or unbounded thread pool, depending on whether you use `send` or `send-off`. In cases where you have enough contention to actually need formal serialization, you don't want either of those. If you have low contention, use an atom. If you have high contention, use atoms and/or pieces of java.util.concurrent.

Fair points, but they are good to get started and can later be replaced with a proper log e.g. Kafka and a worker thread :)

If that's a "proper" replacement, then I think you want a queue, not an agent.

Re: Clojure, the Good Parts

#14
Hm, I've used defs and bindings together inside macros to reduce redundant args like this function here: https://github.com/shriphani/clj-lmdb/blob/master/src/clj_lm...

I think brevity is far more important than being explicit all the time. When we speak, we use a lot of context to infer what is said - no reason code shouldn't look like that.

Re: Clojure, the Good Parts

#15
post #10

It's sad to see that STM is not recommended. It is hugely important in Haskell. I jump to believe that this is an artifact of type-driven STM, but it may be something else instead. I've recently moved from Component to Mount. It is less powerful but just a vast syntactic overhead reduction. Absolutely avoid metadata. The worst thing ever is using it like how Reagent does in ClojureScript where it actually provides im…

Haskell noob here. How is STM used in Haskell? What is type-driven STM?

In Clojure, I don't recommend it because it's Just Another Place To Stick State. It's cool, but provides little semantic value over compare-and-set! on an atom.

Re: Clojure, the Good Parts

#16

Hm, I've used defs and bindings together inside macros to reduce redundant args like this function here: https://github.com/shriphani/clj-lmdb/blob/master/src/clj_lm... I think brevity is far more important than being explicit all the time. When we speak, we use a lot of context to infer what is said - no reason code shouldn't look like that.

The problem with that is that if `put!` or some other function is used inside `map` or some other lazy context, it may get realized outside the context of `with-read-txn`. Making `binding` part of your public API breaks referential transparency.

Re: Clojure, the Good Parts

#17
post #15
post #10

It's sad to see that STM is not recommended. It is hugely important in Haskell. I jump to believe that this is an artifact of type-driven STM, but it may be something else instead. I've recently moved from Component to Mount. It is less powerful but just a vast syntactic overhead reduction. Absolutely avoid metadata. The worst thing ever is using it like how Reagent does in ClojureScript where it actually provides im…

Haskell noob here. How is STM used in Haskell? What is type-driven STM? In Clojure, I don't recommend it because it's Just Another Place To Stick State. It's cool, but provides little semantic value over compare-and-set! on an atom.

Aren't atoms a part of STM in clojure?

Re: Clojure, the Good Parts

#18
Programmers should know their trade-offs. It's not so much that STM should be avoided, but that the use cases it was designed for don't often present themselves in production. Same with agents, core.async, metadata, etc.

So I also get a bit twitchy when I hear for wholesale use of Timbre, Component, Schema. Every one of these libraries has tradeoffs. And those tradeoffs should be understood before adopting them. As an example, if you have a need for Schema, perhaps your data model needs some refining. Maybe you need Schema at the edges of your API, but you should define why you need a library before you just adopt it wholesale.

Likewise with Timbre, often we need tight integration with Java tools, so perhaps my project really does need a "native" java logging library.

Perhaps "understand all the trade-offs" should be the mantra of programming, in any language.

Re: Clojure, the Good Parts

#19
post #12

Earlier quoted context omitted.

Fair points, but they are good to get started and can later be replaced with a proper log e.g. Kafka and a worker thread :)

If that's a "proper" replacement, then I think you want a queue, not an agent.

Like you said an agent is a atom with a queue bolted onto it when only using send. ;P

Btw I think your libraries did the job core.async tries to do a lot better :) and to this date I can't figure out why they didn't received the same amount of attention, so thanks for those!

Re: Clojure, the Good Parts

#20

Pretty good list that I mostly agree with. I think the only point of contention I may have is with using timbre--having tried it in the past, I found it didn't do much that clojure.tools.logging couldn't do for most use-cases, and added configuration complexity while not sparing me the trouble of dealing with logback XML configs and whatnot--Java logging seems to rears its ugly head no matter what you do, especially…

One other thing I will say about Schema (https://github.com/plumatic/schema): while it's a lifesaver in a lot of ways, its mere existence really does expose some of Clojure's deficiencies when it comes to the type system (or lack thereof).

What are good examples of situations where static typing is better than schemas?

Post reply on HN