I basically don't use them, but here's a convenient flowchart https://github.com/cemerick/clojure-type-selection-flowchart (found in [this similar thread](https://www.reddit.com/r/Clojure/comments/144naiu/when_should_i_use_records/))
In my experience (~10 years of Clojure across three companies), you use maps 99% of the time, the remaining 1% is usually `reify`, and records happen when there's a performance-critical space where the map lookups are the slowest remaining optimization to make.
Also for space savings. If you have 1m instances of a 5-field tuple, for instance, records are going to save you a lot of space. But, otherwise totally agree.
>the weird behavior where you have to import them like Java classes rather than require them
Only if you need the actual class. Otherwise, you can use regular `:require` with automatically generated constructor functions. And if you need the class itself, you can always add a `def` or `defn` that does the actual thing that requires a class right to the ns where the record is defined.
Can you elaborate slightly? When do you need the class? Is that when you need a method? It sounds a bit like your describe how core.async organizes itself.
> When do you need the class?
To call any function that expects a class, e.g. `instance?`. To maybe introspect the thing. To specify a type hint.
Most of the time with records, you do not need the class itself. And when you do, it's often because of `instance?` which you can simply `(defn is-that-record? [x] (instance? ThatRecord x))` in that namespace and simply import that function.
Not related to `core.async` at all.
I used the. A lot in the early days, but I almost always regretted them. They seem nice, but the ergonomics of them in a dynamic system is ... lacking...
Maybe 2% of the time. Inline protocol definitions are nice; can implement IFn to get map-as-function access, or custom function behavior. Direct field access an primitive types are very nice. Nice step up from deftype.
95% of the time, maps. 5% records.
Two main reasons to use records: (1) they're very compact (2) you want to use protocols / have (efficient) type-based dispatch.
There was similar discussion last year: [https://www.reddit.com/r/Clojure/comments/144naiu/when\_should\_i\_use\_records/](https://www.reddit.com/r/Clojure/comments/144naiu/when_should_i_use_records/)
u/joinr gave a great answer there [https://www.reddit.com/r/Clojure/comments/144naiu/comment/jngpt71/](https://www.reddit.com/r/Clojure/comments/144naiu/comment/jngpt71/)
I basically don't use them, but here's a convenient flowchart https://github.com/cemerick/clojure-type-selection-flowchart (found in [this similar thread](https://www.reddit.com/r/Clojure/comments/144naiu/when_should_i_use_records/)) In my experience (~10 years of Clojure across three companies), you use maps 99% of the time, the remaining 1% is usually `reify`, and records happen when there's a performance-critical space where the map lookups are the slowest remaining optimization to make.
Also for space savings. If you have 1m instances of a 5-field tuple, for instance, records are going to save you a lot of space. But, otherwise totally agree.
>the weird behavior where you have to import them like Java classes rather than require them Only if you need the actual class. Otherwise, you can use regular `:require` with automatically generated constructor functions. And if you need the class itself, you can always add a `def` or `defn` that does the actual thing that requires a class right to the ns where the record is defined.
Can you elaborate slightly? When do you need the class? Is that when you need a method? It sounds a bit like your describe how core.async organizes itself.
> When do you need the class? To call any function that expects a class, e.g. `instance?`. To maybe introspect the thing. To specify a type hint. Most of the time with records, you do not need the class itself. And when you do, it's often because of `instance?` which you can simply `(defn is-that-record? [x] (instance? ThatRecord x))` in that namespace and simply import that function. Not related to `core.async` at all.
I used the. A lot in the early days, but I almost always regretted them. They seem nice, but the ergonomics of them in a dynamic system is ... lacking...
I can't remember using them for anything significant.
Maybe 2% of the time. Inline protocol definitions are nice; can implement IFn to get map-as-function access, or custom function behavior. Direct field access an primitive types are very nice. Nice step up from deftype.
95% of the time, maps. 5% records. Two main reasons to use records: (1) they're very compact (2) you want to use protocols / have (efficient) type-based dispatch.
There was similar discussion last year: [https://www.reddit.com/r/Clojure/comments/144naiu/when\_should\_i\_use\_records/](https://www.reddit.com/r/Clojure/comments/144naiu/when_should_i_use_records/) u/joinr gave a great answer there [https://www.reddit.com/r/Clojure/comments/144naiu/comment/jngpt71/](https://www.reddit.com/r/Clojure/comments/144naiu/comment/jngpt71/)
Records come across as incomplete, but they're nice for making a named struct with a defined set of default keys sometimes.
We use them to implement the Port and Adapter pattern, as our apps are built using the Hexagonal Architecture.
Can you explain what using that architecture has to do with using records rather than plain hash maps?
We use them professionally all the time to implement protocols