Is it normal to (defn foo ... fn ...) so you can (def func (foo...))?

Hi Everybody,

Recently I wrote several functions like this:

(defn my-function
  [my-input & {:keys [first-optional
                      second-optional
                      third-optional]
               :or   {first-optional  false
                      second-optional false
                      third-optional  false}}]
  (if-let [result (call-something
                   my-input
                   {:first-different-name  first-optional
                    :second-different-name second-optional
                    :third-different-name  third-optional})]
    result
    (call-another my-input)))

They differed only in default values for opts (false in example) and in the last function to call (call-another in example).

I looked at the code realizing that there is a lot of text and a lot of duplicity among those functions but I wasn’t sure how to remove the duplicity since the duplicate code is not something that can be in an another function.

Then I realized I can do (defn foo [x] fn [ ... ] ...) like this:

(defn create-my-function
  [f default-value]
  (fn [my-input & {:keys [first-optional
                          second-optional
                          third-optional]
                   :or   {first-optional  default-value
                          second-optional default-value
                          third-optional  default-value}}]
    (if-let [result (call-something
                     my-input
                     {:first-different-name  first-optional
                      :second-different-name second-optional
                      :third-different-name  third-optional})]
      result
      (f my-input))))

(def my-function1 (create-my-function call-another1 true))
(def my-function2 (create-my-function call-another2 false))
(def my-function3 (create-my-function call-another3 123))

Can anyone please tell me is (defn foo [x] fn [ ... ] ...) a normal thing to do in Clojure?

Thank you.

Best regards,

Billy

2 Likes

That’s the heart of functional programming! Yes, it’s normal :slight_smile: You can also use partial of course (which behind the scenes does exactly the same).

The only issue you can face with above approach is lack of metadata. Lack of arity and argument information precisely speaking. These info is helpful used by editors to provide context help etc.

3 Likes

My eureka moment :wink: Thank you.

I’ve just realized I titled the post partial but I’m actually not using partial and therefore I should keep the arity info.

(I fixed title of this post)

I’m assuming names of the arguments. With (defn foo [bar]....) Clojure knows bar as argument name but with (defn create [x y] (fn [bar].... Clojure will not know bar as argument name…

It’s possible to add in desired metadata, but it requires more effort, of course, and might not be worth it. In one project I added docstrings to things that wouldn’t otherwise have them using this:

(defmacro add-to-docstr
  "Appends string addlstr onto end of existing docstring for symbol sym.
  (Tip: Consider beginning addlstr with \"\\n  \".)"
  [sym addlstr] 
  `(alter-meta! #'~sym update-in [:doc] str ~addlstr))

However, I don’t usually bother with this now. Not sure if it works with recent Clojure versions, or whether it would be considered a bad idea in some respect.

There’s pros/cons to macros, but in this case that’s one reason I think a macro can be useful here.

If your create-my-function is a macro, it can return a proper defn with meta, comments and all that and you can let defn handle all that.

2 Likes