Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keyword arguments vs multi-arity functions #166

Open
erez-rabih opened this issue Aug 5, 2018 · 4 comments
Open

Keyword arguments vs multi-arity functions #166

erez-rabih opened this issue Aug 5, 2018 · 4 comments

Comments

@erez-rabih
Copy link

I would like to propose the following guideline:
Prefer keyword arguments on multi arity when a function allows 2 or more optional parameters to avoid passing nil on function call.

    ;; good
    (defn my-fun [required & {:keys [optional1 optional2]}] ...)

    (my-fun "required" :optional1 "o1")
    (my-fun "required" :optional2 "o2")
    (my-fun "required" :optional1 "o1 ":optional2 "o2")

    ;; bad
    (defn my-fun
      ([required] (my-fun required nil nil))
      ([required optional1] (my-fun required optional1 nil))
      ([required optional1 optional2] (......)))

    (my-fun "required")
    (my-fun "required" "opt1")
    ; nil?
    (my-fun "required" nil "opt2")
@bbatsov
Copy link
Owner

bbatsov commented Apr 30, 2019

I don't know. I usually go with multi-arity when the params for a function are stable. Here's some thoughts that resonate with my perspective https://stuartsierra.com/2015/06/01/clojure-donts-optional-arguments-with-varargs

Any thoughts from someone else?

@erez-rabih
Copy link
Author

erez-rabih commented Apr 30, 2019

I think the difficult thing about more than one optional is that it might make you pass nil values to the function when calling it:

(my-fun required nil 1 nil)

As I see it, function calls with nils in the arguments are less readable and clear. Compare that to:

(my-fun required :optional2 1)

Which doesn't leave you thinking "what are all these nils about"?

@elzibubble
Copy link

FWIW... I agree with erez-rabih that when there are multiple optional and independent arguments, keyword args should be preferred. However, multi-arity should be preferred when each additional optional arg requires the preceding one to be present.

As a trivial example, imagine a dimension function that takes length; width and length; or height, width and length. It doesn't make sense to provide only height, so multi-arity is the correct choice here. Whereas it'd be nice if ex-info took keyword arguments, so you could choose which of msg, map and cause to provide.

You can equally say it's more readable to use keyword arguments, or too verbose. This is up to taste and the situation imo.

A function which takes keyword arguments is more open to extension, whereas a multi-arity function is more prescriptive about what it accepts. Again, both situationally advantageous.

@jgomo3
Copy link

jgomo3 commented May 31, 2019

I think this is relevant: https://clojure.org/guides/destructuring#_keyword_arguments

Also this quote from there:

The use of keyword arguments has fallen in and out of fashion in the Clojure community over the years. They are now mostly used when presenting interfaces that people are expected to type at the REPL or the outermost layers of an API. In general, inner layers of the code find it easier to pass options as an explicit map.

So, the form:

(defn my-fun [required options] ( let [{:keys [optional1 optional2]} options] ... ) )

Is also valid.

Another thing to mention. In all cases, it is possible to set default values. In multi-arity asigning the default value instead of nil on the call to the more-arity version in the less-arity function body. In the keyword way, using the :or option while destructuring.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants