A few things:
I'd suggest that get-by-key
can more clearly be defined as an inline lambda using get-in
instead of implicitly calling the keys. You can also inline the construction of the flag?-bomb?-hint? vector to cut down on definitions.
I wouldn't worry about the filter
immediately going into a set. Sets do one thing and filter does another; the laziness doesn't need to come into play.
Also, this:
(if (g :flags) :flag nil)
is more idiomatic as
(when (g :flags) :flag)
and
#(not (nil? %))
is
some?
Here is a shorter version of your function, taking those into account and not declaring temporary bindings for flag?
, bomb?
, hint?
. I think in this fairly simple scope, that's fine.
(defn all-at
"Returns a set of everything at the position.
Possible contents are :bomb, :flag, or a number representing a hint."
[field x y]
(let [get-at #(get-in field [% [x y]])]
(->> [(when (get-at :flags) :flag)
(when (get-at :bombs) :bomb)
(get-at :hints)]
(filter some?)
(into #{}))))