[HN Gopher] Structuring Clojure applications
       ___________________________________________________________________
        
       Structuring Clojure applications
        
       Author : nefreat
       Score  : 163 points
       Date   : 2022-12-19 14:16 UTC (8 hours ago)
        
 (HTM) web link (yogthos.net)
 (TXT) w3m dump (yogthos.net)
        
       | logistark wrote:
       | For me, protocols i tend to not use it, because it makes it
       | harder to understand the code and Cursive cannot find instances
       | that implements the protocols.
       | 
       | For testing purposes is easier to redef a function than
       | implementing a full new test protocol.
        
         | yogthos wrote:
         | I agree that in most cases with-redefs works fine, and I tend
         | to use protocols sparingly myself for the same reasons.
         | 
         | I thought it was worth mentioning that you can use them to
         | encapsulate any effectful code since they can be used as a tool
         | to help enforce a bit of discipline. If you're just calling
         | functions that can cause side effects then it can get tricky to
         | figure out what all the functions that need to be redefined
         | are. You basically have to read through all the code to know
         | what you need to mock. If you stick all the side effects in a
         | protocol, then you're being very explicit about what needs to
         | be mocked out.
        
       | dwohnitmok wrote:
       | It's interesting to see a lot of FP communities independently
       | arriving at the same architectural structures. See e.g. Haskell's
       | "handle:" https://jaspervdj.be/posts/2018-03-08-handle-
       | pattern.html
        
         | seanc wrote:
         | Convergent design is certainly a thing, but it's also quite
         | possible that communities are cross-pollinating ideas. Either
         | way, when you see those recurring patterns that's certainly a
         | strong indicator that one should pay attention.
        
       | haolez wrote:
       | I'm an experienced developer and I'm getting the feeling that
       | advanced languages are getting less relevant for most
       | applications, since you usually just need a little glue code to
       | glue together mainstream solutions or managed services. I don't
       | need the power of Clojure to connect SQS to Lambda with some
       | extra custom logic.
       | 
       | But Clojure does look amazing :)
        
         | yogthos wrote:
         | For me it's mostly a quality of life issue. I find Clojure
         | workflow is far more pleasant than most languages because it's
         | interactive, and I like Clojure as a language because it's
         | small and focused. I find the main feature of Clojure lies in
         | its simplicity as opposed to advanced features. Clojure code
         | tends to be very direct where you're just passing data through
         | a series of transformations, and you apply a few common
         | patterns to solve a wide range of problems.
        
         | ballpark wrote:
         | Interesting point. I've recently taken on a client who insists
         | on using C# with their cloud solution. It's killing me. Though
         | you're calling Clojure "advanced", what I'm missing is its
         | simplicity.
        
           | haolez wrote:
           | Maybe a better classification would be "amazing languages" vs
           | "good enough ubiquitous languages" :)
        
         | austinbirch wrote:
         | I can see why Clojure is seen that way ("advanced",
         | "powerful"), but a big piece of it's design is Rich Hickey's
         | view that "we can make the same software we're making today
         | with dramatically simpler stuff."
         | 
         | Learning Clojure has made working in other languages much
         | harder for me - pretty much all of my Clojure programming is
         | functions plus transforming data structures (mostly maps). So
         | few concepts to keep in your head!
         | 
         | The majority of my work is in TypeScript these days, and I
         | always write the most Clojure-y TypeScript I can manage.
        
           | haolez wrote:
           | Why not use ClojureScript and transpile it to
           | TypeScript/JavaScript? That's part of my point. It's usually
           | not worth it.
        
             | austinbirch wrote:
             | Yeah, I can see that. Not disagreeing really.
             | 
             | I found ClojureScript worth it a few years ago. I've
             | written two frontend apps (desktop-style interactive
             | single-page applications meant for longer sessions) of
             | reasonable size in ClojureScript. There's a lot of non-glue
             | code there (in fact mostly non-glue code, given the
             | interactivity and statefulness of the applications).
             | 
             | Very little language and library churn has made maintaining
             | them very straightforward really, and I won't be rewriting
             | them any time soon. Being able to use DataScript nearly
             | made the choice worth it on it's own!
             | 
             | The reason I'm mostly using TypeScript these days (for new
             | things & backend code) is that it's just too helpful for
             | typing the data structures and reducing the "how many
             | things do I have to keep in my head" burden. My TypeScript
             | (like my ClojureScript) is mostly just functions and data
             | structures (avoiding classes, inheritance, etc), and I
             | avoid using any of the more complicated TypeScript features
             | as much as possible.
             | 
             | It's kind of heretical, but if Clojure had a well-adopted
             | gradual structural type system (essentially what TypeScript
             | has done for JavaScript) then I would find it hard to not
             | pick Clojure for most things.
        
               | joshlemer wrote:
               | When I was looking into ClojureScript I was kind of
               | concerned at the complexity of writing applications in
               | ReFrame which seems to be what most of the community is
               | using. I've developed apps in this kind of event-
               | emitting/event handler style before in JavaScript and
               | found it quickly got quite out of hand. For my next app I
               | will want to go with something like React-Query that in a
               | sort of declarative way handles all your data fetching
               | for you, and lets you decouple your components from the
               | getting ahold of the data they depend on. I also searched
               | far and wide for some kind of framework/library that
               | supports SSR+CSR like Next.js but I don't think there's
               | anything ready yet except maybe https://github.com/pitch-
               | io/uix.
        
       | rmuslimov wrote:
       | This is very informative and beginner friendly write up which
       | came be used as strategy for organizing apps in Clojure. I
       | personally have something very similar which Redis storage used
       | as persistent storage for storing jobs and tasks within workflow
       | are potentially executed on diff hosts. I would recommend to
       | extending this topic and share your thoughts about
       | component/mount like abstraction to the code. For example notify-
       | sender should have credentials to connect to the services. How
       | they are delivered? as input to the :handle-action method or as
       | as component/mount. Interesting to learn about your approach
       | here.
       | 
       | Thanks for sharing!
        
       | beders wrote:
       | This approach is not simple. It complects a business process
       | state with multi-methods. Don't do it.
       | 
       | Model state where it belongs: in a database.
        
         | yogthos wrote:
         | This approach is perfectly compatible with modelling state in
         | the database. The problem this addresses is the data flow,
         | which is a completely separate problem from managing the state
         | itself.
        
       | twawaaay wrote:
       | I love Clojure and also Common Lisp (basically, Lisp in general).
       | But I also observed every single corporate Clojure project I had
       | any connection with to fail spectacularly. Typically as a
       | complete unmaintainable mess. Some as unmaintainable mess that is
       | very slow and unreliable.
       | 
       | My theory is that this is result of no guardrails on how to
       | structure your application. Clojure to be productive must be used
       | by people who absolutely know what they are doing when it comes
       | to structuring your app.
       | 
       | When it comes to Java you get hordes of devs still producing
       | passable results. The structure is largely imposed by the
       | frameworks (mostly by Spring/Spring Boot) and available help,
       | literature. Even some antipatterns at the very least achieve some
       | level of convention/predictability that is needed to be able to
       | find your bearing around the codebase.
       | 
       | I would say, if you have a normal-ish project, care about
       | productivity but don't have really stellar and mature developers
       | -- skip Clojure.
       | 
       | Choose Clojure if you know how to use all that additional power,
       | have need for it and understand what your added responsibilities
       | are.
       | 
       | If you don't know how to wield Clojure's power there is very
       | little you can gain by choosing it but a lot to loose.
        
         | rockyj wrote:
         | Same problem exists for JS/TS projects btw. You can write
         | anything, anywhere in any style suited to the developer. I
         | guess that is why coporates like Java so much, it commoditizes
         | developers, but can also rock the boat on the other side -
         | cargo-cult programming with no original ideas or innovation.
        
         | bcrosby95 wrote:
         | In general I find Clojure's "power" to be its simplicity.
         | 
         | As you mention, if you're building websites/services, I think
         | the biggest "problem" with Clojure is the community hasn't
         | rallied behind any particular framework.
         | 
         | I'm reminded a bit of companies. The point of a large,
         | successful company is to slow its employees down enough so they
         | don't kill the goose that laid the golden egg. The point of a
         | startup is to find the goose.
         | 
         | When it comes to programming, Java is the former. There's
         | nothing particularly special about Java that keeps you from
         | coloring outside the lines, so to speak. It just makes both
         | coloring outside _and_ inside the lines harder.
         | 
         | Of course, in steps the standard frameworks everyone uses.
         | 
         | I contrast Clojure a bit with Elixir. I find both to be
         | similarly "powerful", in similar and different ways. But the
         | community has rallied behind a web framework (Phoenix) and it's
         | pretty easy for the average web dev to just read a book and be
         | told how they should do everything.
        
           | slifin wrote:
           | I can't remember where but Rich Hickey at one point said
           | himself web development isn't a solved problem
           | 
           | If you agree web development isn't a solved problem, do you
           | want to couple your entire language to a massive framework
           | that is committed to solving an un-solved problem in the
           | general sense using technique X that presumably will need to
           | be replaced Y years down the line? Or not work in situation Z
           | 
           | Personally I don't want that for Clojure, instead I see lots
           | of different approaches in Clojure (and some outside) that
           | could be the next big thing:
           | 
           | - https://www.hyperfiddle.net/
           | 
           | - https://github.com/whamtet/ctmx
           | 
           | - https://github.com/leonoel/missionary
           | 
           | - Unison
           | 
           | Ultimately I want to go long on my programming language and
           | short on "the one true framework" until we have a one size
           | fits all approach for solving the general problem of web
           | development, until then give me low coupling libraries that I
           | can mix and match
           | 
           | And that's exactly what Clojure is doing right now, maybe in
           | the end it will be something like chatgpt just mix and
           | matching the approaches for you but for now I'm personally
           | not looking for a framework to be the only way to use Clojure
        
             | riwsky wrote:
             | Frameworks being imperfect doesn't mean devs can do better
             | without them. The libraries a-la-carte approach makes it
             | very easy to mess up cross-cutting concerns like security,
             | a fact the clojure community has known for a while now:
             | https://m.youtube.com/watch?v=CBL59w7fXw4
        
           | cultofmetatron wrote:
           | I built my startup on elixir.
           | 
           | Once you get past the syntax, a lot of the day to day
           | thinking about how to model your solution in the context of
           | the system is similar. to the point that I'd say elixir is
           | Clojure with a ruby like syntax. elixir has let bindings but
           | they are implicit and blend into the language. you don't
           | really think about them. At the same time, you have all teh
           | standard fare of a functional enough language without all the
           | type ceremony.
           | 
           | Id say its more convenient to work with elixir day to day.
           | Less tracking of parenthesis and the code naturally comes out
           | looking more uniform. There is the price that macros are
           | harder to write in elixir. That is not to say its difficult
           | but lisps in general make writing a macro so damn easy that
           | anything else feels difficult in comparison. I think its a
           | good compromise. Its just difficult enough to make avoid
           | creating macros until you REALLY need them.
        
             | jeremyjh wrote:
             | Elixir doesn't have the same problem of everyone building
             | their own framework each time they build an application,
             | though. Almost every web project uses Phoenix and inherits
             | a whole series of tools and practices that less experienced
             | devs would never think of until it is too late. Every
             | database project uses Ecto with its well-typed schemas and
             | changeset validations etc. Clojure has equivalent things
             | but you have to not only find them, but first find the need
             | for them.
        
         | jwhitlark wrote:
         | Of the two billion-plus exits I've been (peripherally) a part
         | of, the most recent one was Clojure, and the one 10 years ago
         | was Python. I've also seen several Clojure projects and one
         | Scala project fail. With very rare exception, your tools will
         | neither doom you nor ensure success.
         | 
         | It all comes down to execution, in the end.
        
           | thelazydogsback wrote:
           | > Of the two billion-plus exits That's a lot of exits :)
        
           | cultofmetatron wrote:
           | > your tools will neither doom you nor ensure success.
           | 
           | This holds true as long as you're not buidling your startup
           | on something truly outlandish like brainfuck or piet
        
             | thelazydogsback wrote:
             | > Of the two billion-plus exits
             | 
             | That's a lot of exits :)
        
         | mullr wrote:
         | I had the opposite experience. I worked for a company with a
         | bunch of Clojure projects, written by people of varying levels
         | of experience. I had to do some cross-cutting changes and
         | feared the worst. But when I actually got down to it,
         | everything more or less made sense.
         | 
         | Why did this happen?
         | 
         | - We had small common framework that everybody used, at the
         | very highest level (think application lifecycle management).
         | That imposed some amount of consistency at the most basic run-
         | the-program stage.
         | 
         | - The devs communicated openly, a lot, so there was some
         | general consensus on what to do, and what not to do.
         | 
         | - The team at large was very suspicious of introducing new
         | macros. You could do it, but you'd better have a really good
         | reason.
         | 
         | - When I went to make the changes, I didn't have to worry about
         | spooky-action-at-a-distance kinds of consequences anywhere NEAR
         | as much as I do in other languages. Being strict with your
         | state management, as Clojure strongly encourages, REALLY pays
         | off here.
         | 
         | The actual problems I had were entirely related to the overall
         | build system, the fractured nature of the source control, and
         | figuring out who was responsible for what code once we were 3
         | reorgs deep. The code itself was remarkably resilient to all
         | this nonsense.
        
         | mattgreenrocks wrote:
         | > When it comes to Java you get hordes of devs still producing
         | passable results. The structure is largely imposed by the
         | frameworks (mostly by Spring/Spring Boot) and available help,
         | literature...I would say, if you have a normal-ish project,
         | care about productivity but don't have really stellar and
         | mature developers -- skip Clojure.
         | 
         | Regardless of whether I agree, I'm struck by this thinking as a
         | sad commentary on the state of our industry: devs can't be
         | trusted to actually architect the code for maintenance, so they
         | should be forced to color by number with frameworks.
        
           | turtledragonfly wrote:
           | > sad commentary on the state of our industry
           | 
           | Honestly, I find myself structuring _my own_ code to be
           | dummy-proof and paint-by-numbers, even when I am the only
           | user of it (:
           | 
           | I don't see it as a bad thing, necessarily; sometimes you
           | want to "bake in" structure and conventions in various places
           | so you can use your creative energy elsewhere.
           | 
           | Though I do take your point that this is not necessarily (or
           | at all) communicated to junior devs, who are sometimes
           | plopped into a little artificial coding cage and discouraged
           | from reaching outside of it.
           | 
           | Depends on your perspective and intentions, I suppose.
        
           | twawaaay wrote:
           | Faced with decision overload, most people get more productive
           | where most of the decisions are made for them by somebody
           | else.
           | 
           | If you had to consciously make every single decision about
           | everything you do you would die. That's where habits and
           | patterns come to reduce the number of decisions you have to
           | make to a reasonable level.
           | 
           | Most software development projects really are the same as a
           | lot of other projects. You get API services that basically
           | shuttle data between database and web interface, you get
           | frontends which basically shuttle data between APIs and UI,
           | etc. There is no need to invent everything for every project
           | you do, it is much more productive to just focus on things
           | that are specific to your project and adopt the rest from
           | either your experience or some ready made guidebook.
           | 
           | It is not necessarily a lack of trust. It is just that when I
           | hear some development manager to start on a journey of
           | reinventing everything for a pretty mundane backend project
           | it immediately suggests to me a lack of common sense or
           | prioritising personal goals over good of the project.
        
         | mtrimpe wrote:
         | The same applies to most Scala projects from the early days. It
         | attracted many developers who just wanted to have a go at
         | writing their own custom half-baked versions of their favorite
         | parts of category theory for a simple line-of-business CRUD
         | app.
         | 
         | I think both eventually found their niche and now have plenty
         | of developers that stuck around write appropriate code in it.
         | But when they were shiny and new they indeed both created their
         | fair share of failures.
        
         | agumonkey wrote:
         | Interesting take. How much of these architectural problems came
         | from                   - process / workflow (everybody goes on
         | hacking a topic for weeks and then integrate ?)         - team
         | size (I can foresee how clojure would make large team a
         | problem, but small teams happier)
        
         | yogthos wrote:
         | I've had the opposite experience myself. At my last job we
         | switched from Java to Clojure and found that similar types of
         | projects were much faster to develop and easier to maintain. My
         | team also hired a lot of coop students and junior devs that we
         | trained up.
         | 
         | I find the mistake people often make is to get clever with
         | Clojure. Using macros when regular functions would do is a good
         | example of that. It is absolutely possible to write
         | impenetrable Clojure if you start doing weird things just
         | because you can.
         | 
         | However, I find the beauty of Clojure is precisely in the fact
         | that you can write simple and direct code that solves the
         | problem in a clean way without the need to get clever.
         | 
         | My biggest advice for structuring maintainable Clojure projects
         | is to keep things simple, and to break things up into small
         | components that can be reasoned about independently.
        
         | lairv wrote:
         | That goes against the "Beating the average" lesson [0] but I
         | guess this thing is already 20 years old, I wonder if it's
         | still relevant now
         | 
         | [0] http://www.paulgraham.com/avg.html
        
           | twawaaay wrote:
           | There exists no single tool that is best for everything.
           | 
           | Also, not every company can hire best developers (even though
           | most claims so).
           | 
           | Imagine you are project manager for project X which is pretty
           | mundane backend with pretty mundane API and you are given a
           | deadline to develop it.
           | 
           | Will you:
           | 
           | a) try to find absolute best developers on the market and
           | then use absolutely most powerful language (Lisp) to get it
           | done?
           | 
           | b) try to find some developers that are available and use
           | technology that is adequate enough that they can work with
           | not shooting their feet off?
           | 
           | Take into account that you might not be able to hire best
           | developers and even if you do, they might quickly leave not
           | being satisfied with the mundane job you gave them.
        
           | jimbokun wrote:
           | Doesn't beating the average require above average developers
           | according to that article?
        
             | yogthos wrote:
             | My experience is that you need a few developers who know
             | how to use the technology effectively, and who are willing
             | to mentor junior developers. Doing things like pair
             | programming and code reviews goes a long way here.
        
         | beders wrote:
         | Maybe it is you :)
         | 
         | But I understand where you are coming from. Understanding an
         | existing clojure code base is exploratory in nature. You'll
         | REPL into it and run the functions you have difficulties with.
         | 
         | This is a very different activity than chasing object
         | references and hunting down method calls.
        
         | jb1991 wrote:
         | I worked on a large Clojure app for a small company for a few
         | years and it eventually died for similar reasons as you
         | describe. Then, I was hired to work for a startup also
         | developing a large Clojure app, and I must say, it was a very
         | challenging onboarding. The main problem is that "anything
         | goes" in a language like Clojure, which can be great or
         | devastating. My personal take is that the lack of types makes
         | it very hard to understand a large codebase you are exposed to
         | for the first time, unless, as you say, it is structured in a
         | way that was very clear and strict from the outset. Trying to
         | trace the ideas through a large Clojure codebase can become
         | extremely difficult when all they are is a bunch of functions
         | with untyped parameters. Liberal use of spec can help to some
         | degree (though that is also a challenge to enforce, much like
         | optional typing in some languages) but it doesn't change this
         | fundamental paradigm of the language, and I've seen more
         | businesses sunset Clojure than move to it. It's a shame,
         | really, since the language is vastly more flexible and powerful
         | than most, but it goes to show that there are some things that
         | have, in the wild, outsized influence on productivity for the
         | 80%.
        
           | twawaaay wrote:
           | Lack of types -- yeah, I forgot to mention it.
           | 
           | Another is -- lack of good IDE support (which is connected to
           | lack of strong typing). In something like Java you can always
           | understand what is the thing under your cursor.
           | 
           | But an experienced developer knows how to deal with these
           | problems. Lack of strong types means you have to be building
           | clean, well specified interfaces. Lack of strong typing in
           | language does not mean you don't have types in your program
           | -- it just means that it is now on you to make sure it is
           | easy to figure out what is the exact specification of piece
           | of data at any point in the program.
           | 
           | I also found that spec rarely helps. One of the major points
           | of something like Clojure is to be able to create general
           | purpose functions that can do useful operations on very wide
           | range of data and then use those functions to compose your
           | program. Spec really hinders your possibilities here.
           | 
           | > It's a shame, really, since the language is vastly more
           | flexible and powerful than most, but it goes to show that
           | there are some things that have, in the wild, outsized
           | influence on productivity for the 80%.
           | 
           | It is the curse of Lisp. It is the most powerful language
           | that can exist and yet it will never be mainstream because it
           | requires a whole other level of development mastery to really
           | get the productivity benefits.
           | 
           | I personally use Clojure for my rapid prototyping which is to
           | say -- "as long as I am sure nobody else will ever need to
           | work on it with me".
           | 
           | I did a very efficient algorithmic trading framework proof of
           | concept in Common Lisp and it was a joy to work with. But
           | then when it came to actually productionising it I was forced
           | to rewrite it in Java at the cost to performance and heaps of
           | boilerplate code.
        
         | silver-arrow wrote:
         | I initially had the same thoughts as you when I got on my first
         | Clojure project. Previously, I used strongly typed languages
         | with "approved" frameworks. Developed in Java for over 20 years
         | before getting put on a Clojure project.
         | 
         | It because apparent over time that the lack of types and the
         | other features provided by Clojure resulted in much smaller and
         | simpler codebases compared to those previous languages and
         | frameworks that I toiled in for so many years.
         | 
         | It has really highlighted to me the value of simplicity for
         | better productivity and maintainability. I wouldn't even want a
         | framework to build web apps using something like HTMX for
         | example. Clojure handles HTMX in almost magical ways with a
         | simple library or two such as hiccup.
         | 
         | Recently, I was doing some work on a Java / Spring project and
         | was dismayed with the proliferation of classes and packages;
         | really the complexity of it all. And remember, I am solid with
         | Java experience, so it is a result of those types of languages
         | and architectures IMO.
        
           | twawaaay wrote:
           | > It because apparent over time that the lack of types and
           | the other features provided by Clojure resulted in much
           | smaller and simpler codebases compared to those previous
           | languages and frameworks
           | 
           | Totally agree. My very initial motivation that caused me to
           | get interested in Clojure was my study of various API clients
           | generated I think by Swagger Editor? All clients were
           | littered with generated boilerplate -- except for the Clojure
           | one which looked clean and exactly as you would imagine some
           | intelligent programmer writing it. As I already had
           | experience with Common Lisp I immediately understood what is
           | happening here.
           | 
           | > Recently, I was doing some work on a Java / Spring project
           | and was dismayed with the proliferation of classes and
           | packages; really the complexity of it all. And remember, I am
           | solid with Java experience, so it is a result of those types
           | of languages and architectures IMO.
           | 
           | I also have over 20 years of experience with Java and I also
           | share your experience.
           | 
           | Recently I have started mixing OOP with functional and FRP.
           | For example, most of my code is now FRP (ReactiveX/Reactor)
           | and for some strange reason Java is superbly suited to it. It
           | is not a panaceum but I found that I can frequently write
           | what would normally be large features even in minutes.
        
         | dustingetz wrote:
         | i think you're measuring the economic climate of the last two
         | decades and it's impact on management practices,
         | hiring/recruiting etc. which is basically: scale scale scale
         | money money scale, oh and fuck people. Clojure is not for those
         | teams. Clojure is for those of us who reject that
        
       | nkh wrote:
       | Any one tried Polylith with the multi-method approach mentioned
       | in the article?
        
       | dig1 wrote:
       | I am not fond of the multimethods because they can easily tangle
       | the code and give you a false sense of scalability. For example,
       | in a blog post, "handle-action" is nicely decoupled with 3
       | different actions, but let's imagine how that will look after
       | someone adds 20 new actions. Good luck debugging that.
       | 
       | Also, I saw numerous cases where people will copy/paste
       | multimethod arguments without knowing what they are used for.
       | 
       | I still find case/cond more readable and way more performant,
       | especially since the author uses the same type for a multimethod
       | dispatch, but YMMV.
        
         | escherize wrote:
         | Case and cond are nice, but they suffer from the expression
         | problem. When you want to let other people add methods to your
         | code without modifying the source you need to use multimethods
         | (or protocols).
         | 
         | If your method only needs 1 argument then why should the other
         | ones matter? I don't see a problem here. clj-kondo will guide
         | people to name them as _ or _thing anyway so you don't even
         | need to think about it.
        
         | Folcon wrote:
         | case/cond is pretty readable, but I wouldn't say they provide a
         | comparable api to multimethods.
         | 
         | From my perspective, I reach for multimethods when I want to
         | provide my caller with the ability to declare their own
         | "actions" as it were. The goal is to offer an open interface.
         | 
         | Without that need, yes, case or cond can be sufficient.
        
         | fulafel wrote:
         | Multimethods also have some problems vs reloading that need
         | working around.
        
       | TacticalCoder wrote:
       | A similar post, also mentioning protocols and integrant, was
       | posted here a few days ago and may also interest some:
       | 
       | https://mccue.dev/pages/12-7-22-clojure-web-primer
        
       | shaunparker wrote:
       | I haven't looked at Clojure in a bit, but shouldn't the (nil? to-
       | info) check in the transfer implementation of handle-action come
       | first? It seems like that would never be reached in the current
       | implementation.
        
       ___________________________________________________________________
       (page generated 2022-12-19 23:00 UTC)