How to get 'at-least' schema? - clojure

By 'at-least' I mean schema that will ignore all disallowed-key errors.
Consider the following snippet:
(require '[schema.core :as s])
(def s {:a s/Int})
(s/check s {:a 1}) ;; => nil (check passed)
(s/check s {:a 1 :b 2}) ;; => {:b disallowed-key}
(def at-least-s (at-least s))
(s/check at-least-s {:a 1}) ;; => nil
(s/check at-least-s {:a 1 :b 2}) ;; => nil
The first idea about implementation of at-least function was to conjoin [s/Any s/Any] entry to initial schema:
(defn at-least [s]
(conj s [s/Any s/Any]))
but unfortunately such implementation won't work for nested maps:
(def another-s {:a {:b s/Int}})
(s/check (at-least another-s) {:a {:b 1} :c 2}) ;; => nil
(s/check (at-least another-s) {:a {:b 1 :d 3} :c 2}) ;; => {:a {:d disallowed-key}}
Is there's a possibility to get at-least schemas that work for nested maps as well? Or maybe prismatic/schema provides something out of the box that I'm missing?

There is something you can use from metosin/schema-tools: schema-tools.walk. There is even a code that you need in the test:
(defn recursive-optional-keys [m]
(sw/postwalk (fn [s]
(if (and (map? s) (not (record? s)))
(st/optional-keys s)
s))
m))

Related

Clojure custom function for threading macro

I have a map and I want to write a custom function for updating it.
(-> {:a 1 :b 2}
(fn [x] (update x :a inc)))
This of course is a simple example and could be easily done without the function wrapped around the update, but it shows what I want to do. But this gives me the following error.
Syntax error macroexpanding clojure.core/fn at (core.clj:108:1).
{:a 1, :b 2} - failed: vector? at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
{:a 1, :b 2} - failed: (or (nil? %) (sequential? %)) at: [:fn-tail :arity-n] spec: :clojure.core.specs.alpha/params+body
I don't get why this is not working, since the threading macro should but my map as first parameter in the function, right?
You can always use macroexpand to see what happened. In your case, macroexpand will return you:
(fn {:a 1, :b 2} [x] (update x :a inc))
obviously this is not a valid function. But if you tweak it this way:
(-> {:a 1 :b 2}
(#(update % :a inc)))
the expanded form will then become valid:
(#(update % :a inc) {:a 1, :b 2})
You don't put a function itself to be called, but call the function without the first parameter, For your example it would be:
> (-> {:a 1 :b 2}
(update :a inc))
{:a 2, :b 2}
This is easier to see by expanding the macro in each case
> (macroexpand-1 '(-> {:a 1 :b 2} (update :a inc)))
(update {:a 1, :b 2} :a inc)
> (macroexpand-1 '(-> {:a 1 :b 2} (fn [x] (update x :a inc))))
(fn {:a 1, :b 2} [x] (update x :a inc))
As #jas and #rmcv pointed out, I was giving the threading macro the function itself, not the call of a function without the argument. So in short terms the solution would be
(-> {:a 1 :b 2}
((fn [x] (update x :a inc))))
I don't think any of these solutions are the simplest. I would propose choosing one of the following:
A. Use the normal threading form:
(-> {:a 1, :b 2}
(update :a inc)) => {:a 2, :b 2}
Everyone is used to seeing this and can understand it easily. Since you have already rejected this approach, I assume you think the code is clearer by using a named parameter.
B. Use a named function
(defn updater [x] (update x :a inc))
(-> {:a 1, :b 2}
updater) => {:a 2, :b 2}
(-> {:a 1, :b 2}
(updater)) => {:a 2, :b 2}
This is more how the -> form was envisioned to work. I think the 2nd version is the clearest, as it is the most consistent where all function expressions have parentheses (single arg or multi-arg).
C. Consider using the it-> macro from the Tupelo Library:
(it-> {:a 1, :b 2}
(update it :a inc)) => {:a 2, :b 2}
Much like the named function, the expression is normal Clojure form without the "invisible" parameter silently inserted into the update expression. The pronoun it serves as the temporary placeholder for the threaded value (an idea copied from Groovy). Simple, explicit, and flexible, since the it can be in the first, last, or any other parameter location:
(it-> 1
(inc it) ; thread-first or thread-last
(+ it 3) ; thread-first
(/ 10 it) ; thread-last
(str "We need to order " it " items." ) ; middle of 3 arguments
;=> "We need to order 2 items." )

accessing Clojure's thread-first macro arguments

I was wondering if there was a way to access the arguments value of a thread-first macro in Clojure while it is being executed on.
for example:
(def x {:a 1 :b 2})
(-> x
(assoc :a 20) ;; I want the value of x after this step
(assoc :b (:a x))) ;; {:a 20, :b 1}
It has come to my attention that this works:
(-> x
(assoc :a 20)
((fn [x] (assoc x :b (:a x))))) ;; {:a 20, :b 20}
But are there any other ways to do that?
You can use as->:
(let [x {:a 1 :b 2}]
(as-> x it
(assoc it :a 20)
(assoc it :b (:a it))))
In addition to akond's comment, note that using as-> can get quite confusing quickly. I recommend either extracting a top level function for these cases, or trying to use as-> in -> only:
(-> something
(process-something)
(as-> $ (do-something $ (very-complicated $)))
(finish-processing))

Macro to pass patterns and results to core.match/match as a vector

I am struggling on how to construct a macro that lets me pass patterns and results to core.match/match in the form of a vector. I would like to be able to do this:
(let [x {:a 1}
patterns [[{:a 2}] :high
[{:a 1}] :low]]
(my-match x patterns))
> :low
I have tried the below and several other approaches which do not work, unless I pass patterns as a literal.
(defmacro my-match [e ems]
`(m/match [~e] ~#ems))
(let [x {:a 1}
patterns [[{:a 2}] :high
[{:a 1}] :low]]
(my-match x patterns))
=> CompilerException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol, compiling:(*cider-repl kontrakt*:106:10)
(let [x {:a 1}]
(my-match x [[{:a 2}] :high
[{:a 1}] :low]))
=> :low
Macros are expanded at compile time, so you cannot rely on runtime information (the value of a parameter) during expansion. The root problem is that you can't apply a macro in the same way you can apply a function.
In clojure, how to apply a macro to a list?
So you have to either resort to using eval:
(defmacro functionize [macro]
`(fn [& args#] (eval (cons '~macro args#))))
(defmacro my-match [e ems]
`(apply (functionize m/match) [~e] ~ems))
Or approach the problem in a different way (do runtime pattern matching instead of compile time pattern matching).
The simplest way to solve your problem is with a plain old map:
(ns clj.core
(:use tupelo.core))
(def x {:a 1} )
(def patterns { {:a 2} :high
{:a 1} :low } )
(spyx (get patterns x))
;=> (get patterns x) => :low
Since you have no "wildcard values", you don't need core.match at all. If you would like to match on wild-card values, please see the function wild-match? in the Tupelo library. Samples:
(wild-match? {:a :* :b 2}
{:a 1 :b 2}) ;=> true
(wild-match? [1 :* 3]
[1 2 3]
[1 9 3] )) ;=> true
(wild-match? {:a :* :b 2}
{:a [1 2 3] :b 2}) ;=> true

Prismatic schema coercion - renaming map key

I'm trying to coerce a map using prismatic-schema (1.0.4)
I'm trying to coerce
{:a 1}
to
{:b 1}
Using a custom matcher with the schema:
{:b s/Int}
But this code isn't working:
(require '[schema.core :as s])
(require '[schema.coerce :as coerce])
((coerce/coercer {:b s/Int}
(fn [s]
(when (= s s/Keyword)
(fn [x]
(if (= x :a)
:b
x)))))
{:a 1})
Output:
#schema.utils.ErrorContainer{:error {:b missing-required-key, :a disallowed-key}}
I tried debugging it by running the following code which matches everything in the schema and outputs the current value and schema being matched:
((coerce/coercer {:b s/Int}
(fn [s]
(when true
(fn [x]
(println s x)
x))))
{:a 1})
Output:
{:b Int} {:a 1}
=>
#schema.utils.ErrorContainer{:error {:b missing-required-key, :a disallowed-key}}
It looks as though the matcher bombs out as soon as it gets to the map?
Schema first breaks your map up into pieces that match up to the schema, then coerces each MapEntry to the corresponding MapEntry schema, and so on down. This breakdown fails in your case, so you never get to the key.
To accomplish what you want, you'll have to attach the coercion to the map schema and use e.g. clojure.set/rename-keys in your coercion function:
(def Foo {:b s/Int})
((coerce/coercer
Foo
(fn [s]
(when (= s Foo)
#(clojure.set/rename-keys % {:a :b}))))
{:a 1})

Strange thing with apply/merge-with, in Clojure

Who can explain me this fact:
user> ((partial merge-with +) {:a 1} {:a 2})
{:a 3}
user> (apply (partial merge-with +) ({:a 1} {:a 2}))
nil
Why do I get nil in the second case?
What is wrong with the second line?
The expression
({:a 1} {:a 2})
evaluates to nil. Maps in Clojure are functions which takes a key and returns the corresponding value. The expression
(let [f {:a 1}]
(f {:a 2}))
which is equivalent to ({:a 1} {:a 2}) tries to lookup the key {:a 2} in the map {:a 1} and since there is no such key in the map nil is returned.
Going back to your original problem, all you have to do is to change the list ({:a 1} {:a 2}) to a vector [{:a 1} {:a 2}] and it will work as expected. Note also that you don't need partial in this particular case, (apply merge-with + [{:a 1} {:a 3}]) will work just fine.
In the second case, when you ({:a 1} {:a 2}), as maps act as functions which get values from them, what you're doing is equivalent to (get {:a 1} {:a 2}) and, as {:a 2} is not a key in {:a 1}, you get nil. Then, aplying the function over nil gets nil.
What you have to do is either quote the list, such as not evaluate it as a function application
user=> (apply (partial merge-with +) '({:a 1} {:a 2}))
{:a 3}
or use a vector (which is more idiomatic in clojure)
user=> (apply (partial merge-with +) [{:a 1} {:a 2}])
{:a 3}
Juan Manuel