can't provide my own dissoc-like fn
dissoc apparently operates on the map that has the matching key:
(transform {:a 1 :b 2} [:a]
dissoc)
=> {:b 2}
✓
Since dissoc is a fn of (map, key), I ought to be able to provide my own, more selective, fn of (map,key), right?
(transform {:a 1 :b 2} [:a]
(fn [m k]
(if (= (m k) 1)
(dissoc m k)
m)))
ArityException Wrong number of args (1) passed to: normalize/eval1841/fn--1842 clojure.lang.AFn.throwArity (AFn.java:429)
An exact duplicate of the original example above, should be:
(transform {:a 1 :b 2} [:a]
(fn [m k]
(dissoc m k)))
ArityException Wrong number of args (1) passed to: normalize/eval1881/fn--1882 clojure.lang.AFn.throwArity (AFn.java:429)
This prompted me to look at the source, from which I see that the dissoc fn is treated as a special case.
I'd like to be able to provide my own custom dissoc-like fn. Barring that, is there a way to operate on particular associations this way?
I see that a fn in this position only receives one argument: the value of the association:
(transform
{:a 1}
[:a]
(fn [v]
(println "v: " v)
2))
v: 1
=> {:a 2}
That fn can change the value of the association, but cannot dissoc it.
Of course, I gave it a whack with capture groups, but I couldn't make that work.
I think I need to understand your use case better, because it makes no sense to me to supply your own dissoc in this context.
(transform foo [:x :y] dissoc) is just a convenient shorthand for (transform foo [:x] #(dissoc % :y))
I hope I got that right, haven’t done Clojure in a while so am quite rusty :P
Here's my UC. I have a map:
(def m {:x "5" :y ""})
I want a fn that will transform the map, dissoc-ing keys, whose values are blank?. So:
(= {:x "5"} (foo m))
=> true
To do this with transform, the predicate would need to see the collection (associative) and the key, and it would need to be able to return the replacement collection.
As you have shown @boxed, dissoc is a shorthand that hard-codes the value from the path (in your example :y). Maybe if I could stick a fn there like:
(transform m [:x blank?] dissoc)
edited: no that isn't quite right because blank? would apply to the key—but I want it to apply to the value in this case. The brainfart continues…
Then that might become something like:
(transform m [:x] #(dissoc % blank?))
But of course dissoc won't allow a fn as the second parameter alas.
edited: we now return to your regularly-scheduled, mostly-coherent discussion.
Here's a foo that takes a predicate:
(defn foo [m pred]
(into {}
(remove
(fn [[k v]](pred v))
m)))
and it works:
(= {:x "5"} (foo {:x "5" :y ""} blank?))
=> true
I could use foo for a closely-related UC:
I have a map:
(def m2 {:x [1] :y []})
I want a fn that will transform the map, dissoc-ing keys, whose values are empty?. So:
(= {:x [1]} (foo m2 empty?))
=> true
Hmm, yea we don't have any way to match on the values which you've understood is what you're really asking for. One could imagine something like:
(transform m [:x (%>value blank?)] dissoc)
What do you think @crisptrutski ?
Could you branch on the arity of the provided function? If it is 1, then pass in the value at the path as an argument. If it is 2, then pass in the collection and the path as arguments? And explicitly bail out noisily when someone tries to use a multimethod?
That sounds reasonable too. One could imagine supporting arity 3 toF or even more for some other feature. I'm thinking out loud now but how about that the arguments could be: value, key, path, container where key is in. That seems like it would cover lots of use cases.