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

can't provide my own dissoc-like fn #21

Open
Bill opened this issue Nov 15, 2017 · 5 comments
Open

can't provide my own dissoc-like fn #21

Bill opened this issue Nov 15, 2017 · 5 comments

Comments

@Bill
Copy link

Bill commented Nov 15, 2017

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.

@boxed
Copy link
Owner

boxed commented Nov 15, 2017

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

@Bill
Copy link
Author

Bill commented Nov 15, 2017

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

@Bill Bill changed the title can't provide my own dissoc can't provide my own dissoc-like fn Nov 16, 2017
@boxed
Copy link
Owner

boxed commented Nov 17, 2017

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 ?

@devurandom
Copy link

devurandom commented Mar 24, 2019

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?

@boxed
Copy link
Owner

boxed commented Mar 25, 2019

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.

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

3 participants