Why define a factory function for records?

In the swannodettes clojurescript tutorial ( https://github.com/swannodette/lt-cljs-tutorial/blob/master/lt-cljs-tutorial.cljs ), he stated that:

He considered it idiomatic (and recommended) to define a factory function that returns the instantiated defrecord / deftype. It is idiomatic to use dash-case for factory names.

In the example:

(defn person [first last]
  (->Person first last))

      

Why?

The only thing I can think of is using one set of parameters and they don't match the implementation, either as a conversion:

(defn person [full-name]
  (->Person (first (split full-name)) ... ))

      

Or as a guard against implementation changes when used as a library.

What's this?

The drawbacks will be additional, unnecessary features that need to be updated in parallel with implementations and possibly slightly more fuzzy names.

I don't like boilerplate code, so I am always disappointed when I make recommendations like this without explanation.

+3


source to share


2 answers


Java allows multiple constructors with different IE signatures

public class Foo implements Bar {
    private final Boolean initialState;
    public Foo () { this.initialState = false; }    
    public Foo (Boolean initialState) { this.initialState = initialState; }
    public void sayState () {System.out.println(this.initialState)}
}

      

Clojure's constructors cannot be configured this way. Basically you create one generated constructor for yourself based on the field vector that is passed to defrecord

.



(defrecord Foo [initial-state] Bar (sayState [this] (println initial-state)))

      

So, if you want to build an object based on a signature that does not match a field vector defrecord

, you need an fn wrapper to set the default initial state.

And if the field vector from defrecord

ever changes, the factory method will be proof in the future that you don't have to change all occurrences (->Foo state)

in your code.

+1


source


Or as a guard against implementation changes when used as a library.

What's this?

I think that it is.

If your posts are for internal use, just using one of the supplied constructors might be the easiest thing to do. Perhaps you've prototyped maps and realized that you need records for some reason. So there is (maybe) no return ->Record

and map->Record

will be ok.



But when you provide an API to others, it should be as stable as possible. You don't want to surprise consumers and they have to do big refactorings. At the very least, by providing a custom constructor, you can create clear disclaimer alerts.

I usually look at the implementation details of constructor constructors ->Record

and map->Record

and hide them.

TL; DR: It's a different story when you write code yourself, for yourself, and another story when you write a library that could potentially be used by thousands of others.

+1


source







All Articles