[HN Gopher] Abstract Clojure
___________________________________________________________________
Abstract Clojure
Author : joelittlejohn
Score : 151 points
Date : 2021-11-25 13:59 UTC (9 hours ago)
(HTM) web link (www.juxt.pro)
(TXT) w3m dump (www.juxt.pro)
| slifin wrote:
| Reddit conversation here:
| https://www.reddit.com/r/Clojure/comments/r1x1ji/juxt_blog_a...
| charesjrdan wrote:
| Great pointers, I started learning Clojure for a hobby project
| and was initially put off by the lack of 'frameworks' like nextjs
| or rails, but after effectively piecing my own stack together
| from components like Reitit and Integrant I'm really glad I
| didn't use a framework.
|
| I actually feel like I understand everything that happens in the
| system now, and when problems arise I can REPL in and hunt them
| down with confidence.
|
| Though I don't use Clojure professionally, I've definitely become
| a better developer just from playing with it, a contrast to
| JavaScript where I feel like learning it actually lowered my
| iq...
| solmag wrote:
| Rough to read Clojure code without never using it.
| [deleted]
| Zababa wrote:
| > One of the trade-offs with this approach is that it results in
| additional wiring; functions must be passed their dependencies
| and wired together to form a system. The indirection means we can
| no longer jump to the definition of get-article-by-id in the
| server namespace.
|
| That seems like a huge loss. I feel like that approach could be
| great for business logic maybe? There's the "business logic as a
| library" technique that works well for this, and allows for easy
| testing.
| 147 wrote:
| Doesn't it also make the stack traces more difficult to read as
| well?
| amackera wrote:
| I also recommend the book "Grokking Simplicity" by Eric Normand
| for a longer exploration of functional software design (not
| Clojure-specific). The linked blog post uses Clojure examples,
| but this approach to software design is universally applicable
| (especially in functional programming!).
| whalesalad wrote:
| Thank you for this rec, been thirsty for knowledge in this
| domain.
| capableweb wrote:
| Another good one is "Elements of Clojure"
| (https://elementsofclojure.com/) which I think is a slightly
| misleading title. It's a generally good programming book, it
| just happens to use Clojure for it's examples, but I don't
| think it's required to know Clojure to understand the
| concepts explained in it. Also been discussed here before:
| https://news.ycombinator.com/item?id=21090288 and
| https://news.ycombinator.com/item?id=11306519
| slotrans wrote:
| "Software design is a well researched and understood problem..."
|
| I got a good laugh out of that one. Honestly that final paragraph
| should just be deleted. It's just a giant, indefensible claim.
| WJW wrote:
| That really sprung out at me too. I don't think the problem is
| well understood at all, let alone the answers to it.
| asdfasgasdgasdg wrote:
| I'm not sure I agree here. There's not too much value in testing
| these two or three lines of code in the get-article handler. And
| the passing around of compositions of higher order functions can
| get confusing. There are cases where an approach like this is
| warranted but I would say that there needs to be a significant
| level of complexity before this makes sense.
|
| Having a protocol for your data layer and an in memory
| implementation of that for testing can make sense, especially
| when your real data store is expensive to bring up, but I would
| try to minimize the number of seams like this in my system. Each
| one introduces cognitive overhead due to indirection, so you
| should use them judiciously.
| dwohnitmok wrote:
| Yes Clojure is all about first-order functions working on
| simple, concrete data structures.
|
| Pervasive use of protocols and custom, user-defined higher-
| order functions I would argue is unidiomatic Clojure and
| creates long-term pain as you end up with opaque functions
| being passed around that can't be inspected at the REPL and a
| lot of tricky "fitting functions together" that is made
| difficult without a static type system. You sometimes need
| them, but you should reach for them very judiciously.
|
| There's a reason Clojure emphasizes data over functions (and
| both over macros).
| d3nj4l wrote:
| Yes - sometimes you can go too far in the quest for
| abstraction. I've seen a ton of over-enterprisey people build
| gorgeous abstractions with perfect testability and dependency
| management, only for it to be used only ever in one context in
| one way. I lean towards WET first before extracting an
| abstraction, if that.
| [deleted]
| deobald wrote:
| The argument for pure functions holds in a real system; these
| particular signatures will be familiar to anyone who's built a
| few Clojure web services. In reality, some of the functions
| listed (say, `get-article`) would disappear entirely into a
| system-specific convention. There's a balance to be had between
| the number of seams the team is managing directly and those
| which, by their creation, mean _less_ cognitive overhead. The
| seams then exist only if they are required, as certain handlers
| might choose to diverge from the conventional functions used in
| a generic way.
|
| It's of course not easy to see that next step from the article,
| since it doesn't eliminate any code by creating pure functions.
| But even in a toy example, there is value of creating pure
| functional abstractions. In some codebases, you might even see
| the team lead segregate pure functions by namespace: "Pure
| stuff over here, tainted stuff over there." In those
| situations, teams try to reduce impure surface area -- in this
| case, anything that touches the `db` namespace.
| potency wrote:
| What is Clojure's main selling point?
| ByteJockey wrote:
| It's lisp on the jvm.
| vnorilo wrote:
| Sibling provided a good overview of philosophy. Practically it
| offers Lisp goodness, jvm/js interop and a well-designed set of
| persistent collections everyone and their dog uses, part of the
| reason why clj libraries tend to compose very well. You get
| some stunning mileage out of the thing.
| joelittlejohn wrote:
| If you have an hour spare, probably the best way to understand
| Clojure's main selling points is to watch this talk:
| https://www.infoq.com/presentations/Simple-Made-Easy/
|
| InfoQ list the Key Takeaways as:
|
| - We should aim for simplicity because simplicity is a
| prerequisite for reliability.
|
| - Simple is often erroneously mistaken for easy. "Easy" means
| "to be at hand", "to be approachable". "Simple" is the opposite
| of "complex" which means "being intertwined", "being tied
| together". Simple != easy.
|
| - What matters in software is: does the software do what is
| supposed to do? Is it of high quality? Can we rely on it? Can
| problems be fixed along the way? Can requirements change over
| time? The answers to these questions is what matters in writing
| software not the look and feel of the experience writing the
| code or the cultural implications of it.
|
| - The benefits of simplicity are: ease of understanding, ease
| of change, ease of debugging, flexibility.
|
| - Complex constructs: State, Object, Methods, Syntax,
| Inheritance, Switch/matching, Vars, Imperative loops, Actors,
| ORM, Conditionals.
|
| - Simple constructs: Values, Functions, Namespaces, Data,
| Polymorphism, Managed refs, Set functions, Queues, Declarative
| data manipulation, Rules, Consistency.
|
| - Build simple systems by: Abstracting (design by answering
| questions related to what, who, when, where, why, and how);
| Choosing constructs that generate simple artifacts; Simplifying
| by encapsulation.
|
| So Clojure is a language that embodies these principles in its
| design. It's a Lisp, which means that all code is constructed
| from a very regular expression syntax that has an inherent
| simplicity and can be quickly understood. It's a functional
| programming language that provides exceptional tools for
| minimising mutating state, and it favours working with a small
| set of data structures and provides a core api with many useful
| functions that operate on them.
|
| I'd say the result is getting a lot done with a small amount of
| code, minimal ceremony, true reuse, and the ability to maintain
| simplicity even as your system's capabilities grow.
| knubie wrote:
| https://clojure.org/about/rationale
| beders wrote:
| A really well thought out Lisp-1 that runs on the JVM, in the
| browser, in node as well as the CLR and BEAM.
|
| It is hard to go back to other languages once you appreciate
| its simplicity.
| Scarbutt wrote:
| Not writing Java (although you do have to do lots of interop
| with Java).
| capableweb wrote:
| It's true it's "not writing Java", that's true for every
| language besides Java! But the second part is definitely not
| true. First, you can very much use JVM Clojure without
| touching Java, I've done so many times. Secondly, you can use
| ClojureScript which cannot even do interop with Java since it
| "compiles" to JavaScript and doesn't run on the JVM. Thirdly,
| you can use Babashka to run Clojure code with GraalVM and SCI
| instead.
|
| Many options exists to not having to touch Java when you use
| Clojure, but I guess it's hard to kill old memes?
| mark_l_watson wrote:
| Nice article. I have to admit living in the dark ages, Clojure
| wise. I don't even use Protocols, just simple functions and I
| love the simple built in data structures. I was comparing my
| Clojure and Common Lisp libraries for using OpenAI's GPT3 APIs
| last night. I usually use CL, but I notice how much cleaner the
| Clojure version looked (I should refactor the CL version).
|
| Clojure is such a practical language.
| Blackthorn wrote:
| If you use Integrant like they suggest, you don't need to do any
| of that for testability. You can just use the code you had
| originally and have a test function that just creates an
| Integrant system with all fake dependencies. Then you can just
| reuse that test function in every test, occasionally overriding
| one of the fake dependencies.
___________________________________________________________________
(page generated 2021-11-25 23:00 UTC)