Fork me on GitHub

Is it fine to do value transformations using a malli schema then validation using the same schema?


yes, it’s a common pattern to what Plumatic calls Coercion. See for sample code & perf

👍 2

I find myself doing (walk/postwalk walker-2 (walk/prewalk walker-1 user-data)) to transform some xml data into something useable. Is this a smell?


(= (symbol 'foo) (clojure.edn/read-string "'foo"))
;;=> false
I just realized I don't really get how symbols work 😕 What do I need to do to make this work? Context in 🧵


I have data that looks like this:

"[{:tasks [' ' '] :other-key :other-value, :etc :etc},
 {:tasks [' ' '] :other-key :other-value, :etc :etc}]"
and my goal is to remove the items that have .ns.b/baz in the tasks. I can't get the comparison to work though This is the first thing I tried:
(let [data "[{:tasks [' ' '] :other-key :other-value, :etc :etc}, {:tasks [' ' '] :other-key :other-value, :etc :etc}]"]
    (->> (clojure.edn/read-string data)
         #_(map :tasks)
         (filter #(some #{'} (:tasks %)))))
... along with variations that convert the symbols to other types, e.g. string I'm pretty sure I'm missing something but I'm not seeing it.


2 things wrong:


1. edn doesn't support quoting ' -- it is accidental that that doesn't error

💡 1

(= 'foo (edn/read-string "foo"))


quoting is a clojure code thing


in edn, a symbol is just a symbol, there's no eval to resolve it to something


if you want to eval/resolve a symbol in edn, that's up to you


> edn doesn't support quoting ' -- it is accidental that that doesn't error Oh, I didn't know that, thanks so much for the explanation 🙂. Both result in a symbol, so I assumed it doesn't make a difference (I never had to compare them before).

Matthew Twomey20:11:49

Is there a way to destructure a sequence directly into a map e.g.: [1 2 3] => {:value1 1 :value2 2 :value3 3}


Do you want the keys to be value{1stElement}, value{2ndElement}, etc?

Alex Miller (Clojure team)20:11:35

that seems to be the opposite of destructuring

Alex Miller (Clojure team)20:11:20

but (zipmap [:value1 :value2 :value3] [1 2 3])

Matthew Twomey20:11:35

I would like to be able to specify they key names during the destructuring (for a known number and set of values)

Matthew Twomey20:11:51

Ahh ok, not familiar with zipmap, will take a peek at that

Matthew Twomey20:11:45

So while I guess “destructuring” is maybe not the right term, I guess it’s destructure, then “restructure” lol.

Alex Miller (Clojure team)20:11:47

destructuring is about binding locals to parts of a value. as soon as you introduce new things (the keys here), you are beyond what destructuring does

Alex Miller (Clojure team)20:11:14

but that's ok, we've got lots of library functions to do all that :)

Matthew Twomey20:11:28

Really what I want is an efficient way to have

{:a [thing] :b [thing] :c [thing]}
and populate that directly with an array where the first item in the map gets the first value in the array and so forth.

Matthew Twomey20:11:48

But maybe zipmap can do this

Alex Miller (Clojure team)20:11:52

well, zipmap constructs a map that way

Matthew Twomey20:11:01

perfect. Thanks sir for the pointer!

Alex Miller (Clojure team)20:11:19

if you haven't found, you might find it helpful

Matthew Twomey20:11:22

I have thanks - I just somehow missed the zipmap item in there. Still just a few weeks into clojure and a lot to take in 🙂 Having a blast learning though.

❤️ 1
Matthew Twomey20:11:22

I have thanks - I just somehow missed the zipmap item in there. Still just a few weeks into clojure and a lot to take in 🙂 Having a blast learning though.

❤️ 1

Is there a way to get variadic Record constructors?

Alex Miller (Clojure team)20:11:22

the built-in constructors are not variadic but seems like you could easily make one around it (even generically if you plumb the defrecord data), depending on what you actually want

Alex Miller (Clojure team)20:11:14

the whole idea with records is that there are slots are you are filling them, so variadic constructors doesn't really match great with that. if you want partial / arbitrary order, the map->MyRecord constructor seems like an easy path


Wanting a key that is "optional" but defined as a field, ideally being able to define some default, but still useful if it could just default to nil.

Alex Miller (Clojure team)20:11:31

records support arbitrary "extra" keys

Alex Miller (Clojure team)20:11:25

so you automatically have optional, nil values of any possible key


(defrecord Foo [a b])
(->Foo :a :b :c)
; => ArityException


(defrecord Foo [a b c])
(->Foo :a :b)
; => ArityException

Alex Miller (Clojure team)20:11:59

you can not define :c at all

Alex Miller (Clojure team)20:11:01

user=> (defrecord Foo [a b])
user=> (def f (->Foo 1 2))
user=> (keys f)
(:a :b)
user=> (get f :c)
user=> (def f' (assoc f :c 3))
user=> (keys f')
(:a :b :c)
user=> (get f' :c)

Alex Miller (Clojure team)20:11:16

records are open, just like any other Clojure map

Alex Miller (Clojure team)20:11:05

(but optional stuff is slower to access and you may be just as well off with a regular map if you're using extended records a lot - depends if you need the type)


Got it. But I was asking about a variadic constructor, such that I can include / not include a field on construction. I take it you are saying "No, not possible"?

Alex Miller (Clojure team)20:11:05

you can make any function you like :)

Alex Miller (Clojure team)20:11:17

but you won't get that automatically with defrecord

Alex Miller (Clojure team)20:11:28

there are some tricks to macro-ize this kind of thing by utilizing some undocumented basis fields in the generated type as that can tell you what fields exist


Wouldn't know where to start on "undocumented basis fields". (I don't even know what a basis field is. 😛)

Alex Miller (Clojure team)20:11:53

user=> (Foo/getBasis)
[a b]

Alex Miller (Clojure team)20:11:12

it's just the names of the field symbols you created the defrecord with

Alex Miller (Clojure team)20:11:23

so you could for example, write a macro that given a type, could emit a function that called the positional constructor that looked at the count of the basis fields and the variadic args you have, pad them and invoke the positional constructor


Thanks. Sounds like overkill for what I'm working on. But I think I need to make an attempt at that, so I don't lose the context of this discussion.

Alex Miller (Clojure team)21:11:17

for a known record type, it's pretty easy to write the function:

Alex Miller (Clojure team)21:11:41

user=> (defrecord Foo [a b c])
(defn v->Foo [& fs] 
  (let [c (count (Foo/getBasis))] 
    (apply ->Foo (take c (concat fs (repeat c nil))))))
user=> (v->Foo 1)
#user.Foo{:a 1, :b nil, :c nil}
user=> (v->Foo 1 2)
#user.Foo{:a 1, :b 2, :c nil}
user=> (v->Foo 1 2 3)
#user.Foo{:a 1, :b 2, :c 3}
user=> (v->Foo 1 2 3 4)
#user.Foo{:a 1, :b 2, :c 3}

Alex Miller (Clojure team)21:11:53

macroizing that is harder, but definitely possible


For posterity, using map->RecordNameinstead of ->RecordName is sufficient for my needs. Better self-documentation of what's actually being assigned at creation, and no need to mention fields initializing as nil.