For some reason the below spec does say that false isn't a valid ::a-thing even though it is part of the given set.
(require '[clojure.spec.alpha :as spec])
(spec/def ::a-thing #{:a :b :c false})
(spec/valid? ::a-thing :a) ; => true
(spec/valid? ::a-thing :d) ; => false
(spec/valid? ::a-thing false) ; => false
This is nothing to do with spec, and everything to do with how sets behave as functions. You will get similar misunderstandings whenever you use a set as a function to test for membership.
When you pass spec a function, it uses it as a predicate. Sets are functions, as is anything else that implements clojure.lang.IFn. As functions, sets behave as identities on their members. All else follows. Sets are in no way treated specially by spec.
It turns out that falsey things aren't allowed in sets given to Spec
as it uses the set itself as a function to check for membership rather than
the contains? function. As we can see below a set will return the
given argument if it is a member of the set and nil otherwise.
(#{:a :b :c false} :a) ; => :a
(#{:a :b :c false} false) ; => false
(#{:a :b :c false} :d) ; => nil
This of course is the cause of the misunderstanding.
We have to manually wrap the set in a contains ourselves in order to get the spec working properly.
(spec/def ::a-thing #(contains? #{:a :b :c false} %))
Related
Write a function which, given a key and map, returns true if the map contains an entry with that key and its value is nil.
Solution I came across:
#(nil? (get %2 % true))
Can someone please explain the use of true in
(get %2 % true) ?
Thank you!
that's a default value that will be returned in the case of absence of the key
;; key exists
(get {:a 1} :a 2)
#=> 1
;; key doesn't exist (default value is returned)
(get {:a 1} :b 2)
#=> 2
;; key exists and it's value is nil
(get {:a nil} :a 2)
#=> nil
;; key doesn't exist, nil is returned
(get {:a 1} :b)
#=> nil
some docs could be found here
https://clojuredocs.org/clojure.core/get
so the idea is that (get {:a 1} :b) will always return nil because key doesn't exist. In this case (nil? (get {:a 1} :b)) will return true, which is not what we want. That's why adding this default value is necessary. So nil will be returned only in the case when the actual value is nil.
The value true is not special here. 85 would work just as well: any value other than nil fixes the problem.
The following clojure spec ::my permits maps having either the key :width or the key :height, however it does not permit having both of them:
(s/def ::width int?)
(s/def ::height int?)
(defn one-of-both? [a b]
(or (and a (not b))
(and b (not a))))
(s/def ::my (s/and (s/keys :opt-un [::width ::height])
#(one-of-both? (% :width) (% :height))))
Even if it does the job:
(s/valid? ::my {})
false
(s/valid? ::my {:width 5})
true
(s/valid? ::my {:height 2})
true
(s/valid? ::my {:width 5 :height 2})
false
the code does not appear that concise to me. First the keys are defined as optional and then as required. Does anyone have a more readable solution to this?
clojure.spec is designed to encourage specs capable of growth. Thus its s/keys does not support forbidding keys. It even matches maps having keys that are neither in :req or opt.
There is however a way to say the map must at least have :width or :height, i.e. not XOR, just OR.
(s/def ::my (s/keys :req-un [(or ::width ::height)]))
This feature is built into spec - you can specify and/or patterns in req-un:
(s/def ::my (s/keys :req-un [(or ::width ::height)]))
:user/my
user=> (s/valid? ::my {})
false
user=> (s/valid? ::my {:width 5})
true
user=> (s/valid? ::my {:height 2})
true
user=> (s/valid? ::my {:width 5 :height 2})
true
Just wanted to pitch in with a small modification to the spec in the original question which logic will fail if the value held by any key is falsey, i.e. false or nil.
(spec/valid? ::my {:width nil})
=> false
Of course this won't occur with the given constraints of int? put on the values in the question. But perhaps someone in posterity allows their values to be nilable or boolean in which case this answer becomes handy.
If we instead define the spec as:
(defn xor? [coll a-key b-key]
(let [a (contains? coll a-key)
b (contains? coll b-key)]
(or (and a (not b))
(and b (not a)))))
(spec/def ::my (spec/and (spec/keys :opt-un [::width ::height])
#(xor? % :width :height)))
we get the result that
(spec/valid? ::my {:width nil})
=> true
(spec/valid? ::my {:width nil :height 5})
=> false
I am doing a clojure excersice and this is one question.
(true? (__ :a {:a nil :b 2}))
the answer is :
#(nil?(%2 %1 0))
But I could not get it.
What does %2 %1 0 do here?
Especially, what does that 0 do there?
can you explain it for me?
thanks
The #() format is shorthand for defining anonymous functions.
For example: #(+ %1 %2) is the equivalent of (fn [a,b] (+ a b))
Here %2 and %1 represent the second and the first argument to the function respectively.
As it was mentioned earlier,
#(nil? (%2 %1 0)) is a sugar for
(fn [a-map a-key] (nil? (a-map a-key 0)))
A few things to notice about this line:
hash maps in clojure support the function semantics, so using the map in place of function is identical to calling get function
(a-map a-key default-value) equals to (get a-map a-key default-value)
zero as a default key is added to distinguish the abscence of key from a nil value of key. For example:
(get {:b 1} :a) produces nil (as there is no :a key in the map)
(get {:a nil :b 1} :a) also produces nil (as nil is a value of :a)
while using the default value removes this uncertainty:
(get {:b 1} :a 0) => 0
(get {:a nil :b 1} :a 0) => nil
the map in the example has numeric values, so using zero as a default value can lead to even bigger mess (well, depending on the use case. Sometimes zero is exactly what you need here). It is common to use some value that can't be there, for example a keyword like :not-found
But in this case you would probably want to do the task this way:
(true? (#(contains? %2 %1) :a {:a nil :b 2}))
As they obviously want you to check if the key :a is present in a map
I'm trying get a clojure function to detect if the value passed is a map.
For example,
user=> (if-map {:foo 1}) ;Should return true
true
user=> (if-map "hello") ;Returns false
false
Is there a pre-built function serving this already?
Yes, map? is the inbuilt function
(map? {:a 1})
=> true
(map? [1])
=> false
Given a map {:a 1 :b [2,3]}, is there a built-in function which would return the sequence (:a 1 :b [2,3]).
The use case is applying an options map to a function which does map-destructured binding on the remainder of an argument list. Here's an example of this in core.cache. Here's a contrived example to illustrate:
(defn make-car [& {:as options}] (assoc options :car true))
(make-car :color "red" :speed "fast")
; => {:car true, :speed "fast", :color "red"}
Now if we want to manage the options separately and apply them to the function, we have a problem:
(def options {:color "red" :speed "fast"})
(apply make-car options)
; => {:car true, [:speed "fast"] [:color "red"]}
...because of course the seq of a map is a sequence of its key-value pairs. This is the best I've come up with:
(apply make-car (interleave (keys options) (vals options)))
; => {:car true, :speed "fast", :color "red"}
This is pretty awful. I know I could just make my own function to do this, but I'm surprised I haven't found something built-in. If there isn't something built-in, then I'd probably want to avoid destructuring argument lists like this in library code.
How about this:
(reduce concat {:a 1 :b [2,3]})
(:a 1 :b [2 3])
Update based on the comment from amalloy.
Apply is more efficient (at least in 1.4), and achieves the same result!
(apply concat {:a 1 :b [2,3]})
(:a 1 :b [2 3])