Using `set` just to let people know your intention

Hello Clojure devs,

One thing is bugging me and I can’t get answer anywhere else. Could you please help?

I wrote some functions like this in past:

(defn filter-cars
  [cars & {:keys [color
                  engine
                  make]
           :or   {color  [:red :blue :green]
                  engine [:big :small]}}]
  ,,,
  )

And recently I read some code on Github (unfortunately I don’t remember the author) and it was something like this

(defn filter-cars
  [cars & {:keys [color
                  engine
                  make]
           :or   {color  #{:red :blue :green}
                  engine #{:big :small}}}]
  ,,,
  )

There is no extra functionality in either the author code or in my code that would take advantage of the set (we don’t do something like (#{:blue :red} :red)). Any collection would do the job. But I realized that the author (maybe) is giving a clue that the collection elements should be unique / that :red twice wouldn’t return more cars.

Would you use set in this example to add a bit of documentation without actually writing docstring or comment?

Thank you.

Alan

4 Likes

It might be that the set is being used elsewhere in the codebase and the function is giving the defaults.

ie. what the input of the function expects the color key to be.

Also are you sure that there is no (filter #(-> % :color color) cars) thing going on? Sets are really handy for filtering and I’d assume that’s what it’s being used for.

Thank you @zcaudate . It is possible that the set was used as a function somewhere but I didn’t notice… But let’s assume it was not used and that [,,,], #{,,,} and '(,,,) would work same. Would you use set to indicate something?

1 Like

Personally… not the way you described. If anything I’d make things clear in test cases or the function documentation.

It’s really hard to infer intent from other people’s code. I wouldn’t over think it.


Also, stylistically, I prefer a single map [& [opts]] over map vargs as it composes better. This is especially the case when I don’t know exactly what keys there are going to take and it may be necessary to pass the opts map around in between functions.

The choice of which datastructure to use can significantly impact how the code performs. So, it should be documented what is expected and probably even checked or automatically transformed. These are not equal, and will most definitely not work the “same”.

1 Like

I would use a set if I want set semantics yes.

Is it important invariants that there are no duplicates and that order is random? And therefore you don’t want people to take coupling on those properties such as implementing logic that depends on the order or supports duplicate?

It’s also a bit weird that you say it’s not used anywhere, like at least your function body mist be using it, would it not benefit from a set for filtering?

2 Likes

I sometimes use sets just to communicate that

  • I don’t want people to rely on ordering for this thing
  • I don’t want duplicate elements.
1 Like

Yes, no duplicates and the order is random.

In the case I asked about, really any collection would do the job.

I realized that I use [] because it’s one character less to type. But by using it I also communicate that the order is important and that duplicates might do something.

I asked about using set, but the proper question should be using vector if I don’t use vector characteristics… The answer is of course I should not use vector if I don’t need ordering / duplicates.

Thank you @didibus and @teodorlu . I won’t default to as I did till now… I’ll use [] / #{} based on their functionality or to communicate order and duplication of the elements.

1 Like

To be fair, I think using a vector is perfectly fine in most cases too.

Glad you found it helpful!