how meander is better than specter when restructuring data https://github.com/redplanetlabs/specter/issues/277#issuecomment-698847230
https://www.reddit.com/r/Clojure/comments/izh9o4/specter_vs_meander_vs_handcrafted_code_when/
I'm not sure if the manual version there is charitable...
by charitable you mean ..?
Well I mean without any sort of let
or letfn
I would think something like this would make a little more sense:
(transduce
(map (juxt :id #(select-keys % [:pick-me/b :pick-me/c])))
(completing
(fn [res [id m]]
(assoc res id (into {} (map (fn [[k v]] [(-> k name keyword ) v])) (seq m)))))
{}
data)
there may be others like pick-me/d in addition to pick-me/b and pick-me/c , so you can not assume there are only these two such keys
but the point is not how to best implement the handwritten version, but that no hand written version could be as expressive as using meander
Oh for sure. But if you want to make a strong point, you need to pick the strongest possible counterargument
No doubt the meander is more elegant. But I would just fear that someone might think the example was constructed to make vanilla clojure seem more inelegant than it is (Edit: I'm an idiot, disregard)
tbh the hand-crafted code is almost the best version I could write ..
😬 I'm sorry, I'm an asshole, I didn't realize you wrote that
nvm 😄
Well I can't use specter at all so maybe I was just trying to make myself feel better, I didn't mean for it to come across like that 🙏
🙇
I'm trying to copy the functionality and syntax of metosin/malli
using meander. However, I came across behavior that I don't understand.
I probably don't know how macros work, but I can't figure it out myself; (
(require '[meander.epsilon :as r])
(require '[taoensso.encore :as e])
(r/defsyntax ->schema [schema]
(r/match schema
[:map . (r/cata !x) ...] (r/subst {& [!x ...]})
[(r/or (r/pred keyword? ?k)
(r/pred string? ?k))
(r/cata ?v)] {?k ?v}
?f `(r/pred ~?f)))
(r/match {:a 1.0 :b 1.0}
(->schema [:map [:a double?] [:b double?]]) true
_ false)
;; => true
(def x [:map [:a double?] [:b double?]])
(e/caught-error-data
(r/match {:a 1.0 :b 1.0}
(->schema x) true
_ false))
;; => {:err-type java.lang.IllegalArgumentException,
;; :err-msg "Key must be integer",
;; :err-cause nil}
@wxitb2017 FYI there is m/keyword
for pattern matching on keywords. 🙂
(m/rewrite data
[{:id !id (m/keyword "pick-me" !k) (m/some !v)} ...]
{& ([!id {!k !v}] ...)})
So I'm not sure how to solve this problem. I'm trying to play around with it. But what is happening is that the x
there is actually just the symbol x
. It isn't the value sliced in. That is what is causing the error. Trying to think about how you would do that, not really sure.
The trouble here is that the value x
in
(->schema x)
is not passed to the ->schema
syntax extension function, the symbol x
is. So what happens is that the extension expands to (x {:a 1.0 ,,,})
or
([:map [:a double?] [:b double?]] {:a 1.0 ,,,})
and thats why you get the error. If you want the extension to get the value you’ll need to use eval
.(m/match {:a 1.0 :b 1.0}
(->schema #'x) true
_ false)
Seems to work.You do have to add m/keyword around the !k to get the same output, just fyi.
(m/rewrite data
[{:id !id (m/keyword "pick-me" !k) (m/some !v)} ...]
{& ([!id {(m/keyword !k) !v}] ...)})
Really love all the comparisons you are doing here. Thanks for doing this :)
@jimmy I don't know why, but it doesn't work for me. Exactly the same error.
@noprompt I don't know where to use eval
, could you give an example?
EDIT:
Already found, it works, thanks!
EDIT:
... and it's incredibly slow. It's a pity.
Sorry, bad repl state
What did you end up doing? What performance are you getting? What would you expect it to be?
Meander is amazing and I play without any purpose. I check how little code is needed to get functionality of other libraries.
When I say it's incredibly slow, I meant eval
Glad to hear 🙂 Just always on the look out for potential performance issues. Once jit kicks in, I get the exact same performance of these two
(m/defsyntax ->schema [schema]
(m/match (eval schema)
[:map . (m/cata !x) ...] (m/subst {& [!x ...]})
[(m/or (m/pred keyword? ?k)
(m/pred string? ?k))
(m/cata ?v)] {?k ?v}
?f `(m/pred ~?f)))
(def x [:map [:a double?] [:b double?]])
(defn validate-1 [data]
(and (map? data)
(double? (:a data))
(double? (:b data))))
(defn validate-2 [data]
(m/match {:a 1.0 :b 1.0}
(->schema x) true
_ false))
So it seems that I used eval
not in the right place. ; )
However, it seems that a more generic function cannot be done. An exception Can't eval locals
is thrown.
(r/defsyntax ->schema [schema]
(r/match (eval schema)
[:map . (r/cata !x) ...] (r/subst {& [!x ...]})
[(r/or (r/pred keyword? ?k)
(r/pred string? ?k))
(r/cata ?v)] {?k ?v}
?f `(r/pred ~?f)))
(defn valid? [schema data]
(r/match data
(->schema schema) true
_ false))
Yeah. ->schema is being expanded at compile time. And schema isn't known at that point.
We have plans for an interpreter that would allow more dynamic things. But obviously that would be slower.
It is still awesome