Fork me on GitHub
#meander
<
2020-09-25
>
Lucy Wang10:09:00

how meander is better than specter when restructuring data https://github.com/redplanetlabs/specter/issues/277#issuecomment-698847230

🙂 3
Jimmy Miller16:09:22

Really love all the comparisons you are doing here. Thanks for doing this :)

Lone Ranger14:09:04

I'm not sure if the manual version there is charitable...

Lucy Wang14:09:01

by charitable you mean ..?

Lone Ranger14:09:28

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)

Lucy Wang14:09:45

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

Lucy Wang14:09:56

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

Lone Ranger14:09:31

Oh for sure. But if you want to make a strong point, you need to pick the strongest possible counterargument

Lone Ranger14:09:27

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)

3
Lucy Wang14:09:32

tbh the hand-crafted code is almost the best version I could write ..

Lone Ranger14:09:35

😬 I'm sorry, I'm an asshole, I didn't realize you wrote that

Lone Ranger14:09:19

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 🙏

😎 3
ribelo15:09:21

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; (

ribelo15:09:45

(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}

Jimmy Miller16:09:11

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.

noprompt16:09:10

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.

Jimmy Miller16:09:51

(m/match {:a 1.0 :b 1.0}
  (->schema #'x)  true
  _             false)
Seems to work.

ribelo16:09:54

@U5K8NTHEZ I don't know why, but it doesn't work for me. Exactly the same error.

ribelo16:09:52

@U06MDAPTP 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.

Jimmy Miller17:09:17

Sorry, bad repl state

Jimmy Miller17:09:09

What did you end up doing? What performance are you getting? What would you expect it to be?

ribelo17:09:25

Meander is amazing and I play without any purpose. I check how little code is needed to get functionality of other libraries.

ribelo17:09:57

When I say it's incredibly slow, I meant eval

Jimmy Miller17:09:43

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))

ribelo17:09:30

So it seems that I used eval not in the right place. ; )

ribelo17:09:45

However, it seems that a more generic function cannot be done. An exception Can't eval locals is thrown.

ribelo17:09:03

(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))

Jimmy Miller17:09:51

Yeah. ->schema is being expanded at compile time. And schema isn't known at that point.

Jimmy Miller17:09:23

We have plans for an interpreter that would allow more dynamic things. But obviously that would be slower.

ribelo17:09:38

It is still awesome

noprompt16:09:07

@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}] ...)})

👍 9
Jimmy Miller16:09:29

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}] ...)})

Lucy Wang01:09:12

Thanks! I have updated the code in the posts with this better approach.