Discussion summary

Clojure 1.13 introduces support for checked keys, enabling runtime argument validation. Developers see it as a helpful addition, though some note it’s not static typing. The syntax remains challenging for newcomers but becomes more familiar over time.

What the discussion says

  • Some see it as a useful runtime check, similar to assertions.
  • Others highlight the difficulty in reading Clojure syntax initially.
  • Many users mention a learning curve but eventual familiarity.
  • The update is considered beneficial for code correctness.
This is helpful, because many functions have assert-like checks.
pgt
It took about two weeks to read Clojure syntax comfortably.
erichocean

Join the discussion

Write your take first — we'll ask for email only when you're ready to publish.

  • Hacker News
  • Slowly but surely dynamic programming proponents discover the value of statically verifiable correctness. Who'd have thought?
  • Oh we know. That's why static types are a la carte in Clojure. You can have them if you really really want them.
  • Snarkiness aside, this is hardly a static type check. This appears to be a runtime argument check, somewhat akin to the following python:

      def foo(*, a, b): return a+b
    
    which errors out at runtime if `a` or `b` are omitted, despite being keyword arguments which are usually optional.
  • Ah yes, the missing seventeenth way to validate function parameters.
    by thom
  • This is helpful, because practically many functions in the wild have assert-like checks at the top of the function, e.g.

    `(if-not key1 (throw (Exception ...))`

    ...and pre-conditions, e.g. `:pre [condition1 condition2]` do not run when `assert` is off.

    by pgt
  • We just updated one of our projects to 1.12.5, but I might push for 1.13 as this could be very useful, although an alpha version might raise questions.
  • I love the idea of clojure and perfect immutability, but holy crap I cannot grok the syntax. My C-trained brain explodes.
  • It'll take a while but now other programming languages look alien to me. Once you've adopted s-expressions it is hard to go back.
  • I went through a similar phase decades ago with Common Lisp. It takes a week or two. Now, it’s quite a natural syntax and I see the parens as a huge benefit. I like Clojure syntax even more than CL and Scheme because of the map and vector literals.
  • It took about two weeks for me to be able to read it.

    Might as well have been Russian.

    Now it's as natural as any other language.

  • Lisp is tricky. Pretty much every programmer for whom it's not their very first PL hates it initially, but then there's a time, a threshold after which no other language feels more readable than Lisp.

    Using structural editing idioms and the REPL, usually makes the process less vexing.

  • It's like a light saber instead of a machine gun. Let the bullets come to you.
  • You get over it really quickly once you start actually using it. I find it basically impossible to read Clojure outside the editor in any meaningful sense.
  • Is it only me or this sounds a bit counter to clojure philosophy?
    by ndr
  • Howso?
  • I agree it feels a bit counter to the philosophy of Clojure. It's adding new syntax to the language for map destructuring only (that will need to be implemented in cljs and other runtimes for consistency) and it's a purely runtime check as we don't know the map's keys at compile time. I don't see what new kind of safety it adds that's not achievable with existing solutions such as :pre or doing an assert inline.

    I feel there are better solutions to this problem that already exist, such as using spec/malli and validating the value properly rather than just checking for presence.

  • It's 100% opt-in at the call site and doesn't affect existing code, so no?

    Many people (including myself) already have checked key variants for maps; this mainly extends the syntax to destructuring too.

  • Seems additive to me; no breaking changes, and better control and error messages when opting in for it, seems entirely Clojurely to me.
  • The maps are still open to new keys even if some keys are checked. I think that fits in with how clojure.spec and Malli work already, but in a lighter syntax.
    by rads
  • As a Clojurist the standard pattern for ensuring keys-are-set before doing-something is not-as-elegant-as-this. Clojure is full of macros that do useful things :) Simplifying oft-used patterns into compact representations is very on-brand. Plus, you need this like, all the time.

    This will eliminate two whole classes of errors: 1) where keys are supplied a value at an undesired nesting-level. 2) where keys are not-yet-set for some other reason.

    For the many programmers who have to write in checks and verifications themselves for this, this saves quite a bit of time, removing the interruption from coding and restoring the flow of getting logic-to-symbol.

  • This is actually great, and I predict that fans of nil-punning will rapidly discover the joys of actually having errors trigger where the error was introduced rather than propagating through the program.

    Any news on ClojureScript gaining the feature?

  • Well, kind of. This is the kind of problem that you might throw schemas (eg. Malli) at pre 1.13.

    It’ll be nice to have it at hand in the base language though.

  • What is nil-punning?
  • Working on it :)
  • Some explanations from https://clojure.atlassian.net/browse/CLJ-2961:

    > Clojure’s idiomatic use of maps has proven valuable, but missing required keys, misspelled keys, and invalid values can lead to failures that do not connect to the actual source of the problem (e.g. NPEs) making diagnosis difficult. At the same time, Clojure lacks a simple inline mechanism for functions to document and check the keys they require and accept. Existing tools either separate those expectations from the function itself or couple data shape and data provision.

  • One thing to note that's maybe less obvious is that you can destructure some keys with the check and others without. This makes the function interface a bit self-documenting. At a glance you see that the username is required and other parts are maybe not.

        (defn my-function
          [{:keys! [username]
            :keys  [firstname
                   lastname]}]
          (do-stuff username
                    firstname
                    lastname))
    
    A minor downside is that now it seems `nil` is even more overloaded b/c you can explicitly pass in a nil and give it a special meaning. This generally cascades in to messyness (better to have a special key like `:missing-username`).

    Feels like throwing an error on nil would have been better/simpler? But I'm sure there's an angle I've not considered

  • This is a case I never really thought about - if the key is missing today you'll get nil as the value and since Clojure is a nil punning language it usually does sensible behaviour in your program

    I know this sounds unreliable but in practise I like a language that defaults to pragmatic code paths so I don't have to stay up at night imagining a million code paths

    This adds a throwing codepath which is quite drastic so I'm glad people don't build this into programs everywhere - I'd be nice to hear what the team imagine as the use case for this

    Normally for correctness I'd like to see specs at the boundaries for programs and different test suites for internal behaviours

  • > if the key is missing today you'll get nil as the value

    You can add a third parameter to override the nil if detecting the missing key matters.

    (You almost surely know this, but not all HN commenters will.)

      user> (:bar {:foo 1})
      nil
      user> (:bar {:foo 1} :missing)
      :missing
  • Yeah I'm with you, this feels like a case for assertions & not a new core feature. Perhaps I'm missing something because it was promoted by Michael Fogus and has been ratified by those who would know (Alex I'm assuming). To me it doesn't pass the test of necessity as something needed at the core but at least it feels somewhat ideomatic.
  • For me: documentation at the "front door" of an interface, especially in that long moment before you decide to add a spec or Malli schema.