Fork me on GitHub
#clojure-spec
<
2019-01-15
>
favila01:01:31

This was something I wrote to tackle the specific case of having a more general spec (as a key in a map) that context (what kind of map it is in) would narrow

favila01:01:28

e.g. :a could be 1 2 or 3, but when it's in a map with :map-type "foo" :a must be 1 or 2

favila01:01:01

dealing with that case over and over with predicates was a hassle, so this macro (keys+) allowed adding an additional keyword->spec mapping for validity, conformance, and generator purposes

favila01:01:20

but the original "natural", "contextless" spec still had to validate

favila01:01:33

so it's not aimed at the DSL use case, this was to deal with data from a foreign system that had a kind of tagging/inheritance relationship. their fields and entites had a base definition that was overly broad, and could be narrowed without changing their structure

favila01:01:26

think xsd facets where the element names don't change

favila01:01:14

the purpose was to allow generic processors to understand everything, but particular producers/consumers to add additional constraints about what they would produce/consume

favila01:01:44

e.g. a field is cardinality-many, but some processor guarantees they only ever put one value in that field

misha12:01:30

@cjsauer you can s/or or s/alt lookup-refs spec with s/keys. Or avoid speccing maps with lookup-ref values (`{:my/attr [:db/id :db/ident]}`). Or, if you can isolate lookup-ref key-values from actual data key-values, spec lookup-refs with key-agnostic spec, something like (s/map-of qualified-keyword? lookup-ref-spec). I don't think having s/select would solve spec overload (data or lookup) in this case, because: https://clojurians.slack.com/archives/C1B1BB2Q3/p1547474311308100

misha12:01:59

Another option is to have s/or + (s/map-of keyword? not-a-look-up-value?) where you expect already looked-up values.

ShaneLester16:01:13

So I have [org.clojure/test.check "0.9.0"] in my dependencies, but when I’m trying to use generators with spec I’m still getting a clojure.test.check.generators never required error…. Anyone have an idea of what I should try? Is there a different one for cljs potentially?

borkdude16:01:23

have you tried requiring it manually?

borkdude16:01:29

@shanelester55 are you on CLJS or CLJ and which version?

ShaneLester16:01:58

I’m on CLJS. Using shadow-cljs so… not positive which version. Whichever version it is currently using, which I imagine is the latest.

ShaneLester16:01:10

Is requiring it manually just specifiying it at the top of the file where it is used?

borkdude16:01:44

you should be able to know the CLJS version in order to deal with known issues. can you try *clojurescript-version* in the REPL?

borkdude16:01:02

there were some changes around this recently, but they are only on master, not released yet, that’s why I’d like to know

ShaneLester16:01:25

Sure thing. trying that now… for some reason giving me trouble

borkdude16:01:56

if you don’t have a REPL, just print it the console from your app

borkdude16:01:57

or type cljs.core._STAR_clojurescript_version_STAR_ in the browser console

ShaneLester17:01:28

Ah that worked easily enough. "1.10.439"

borkdude17:01:52

ok, how do you use the generators. using clojure.spec.test.alpha?

ShaneLester17:01:38

Currently using these

borkdude17:01:57

ok, I imagine you want to call gen/sample to just see some generated data right?

borkdude17:01:34

you’ll need a require to [clojure.test.check.generators] in your ns as well

ShaneLester17:01:08

Ahhh that did it.

ShaneLester17:01:12

Thank you very much

borkdude17:01:43

actually just [clojure.test.check] should work too

borkdude17:01:26

since it requires all the other needed namespaces

ShaneLester17:01:50

Makes sense. That indeed works as well.

ShaneLester17:01:53

Thanks again 🙂 Works well now

devth17:01:50

reposting my notes on spec-related content in the 'Maybe Not' talk (https://www.youtube.com/watch?v=YR5WdGrpoug) since the thread is buried. 1. separating out schema (shape) and selection (optionality). cool stuff! - it's still not "just data". why the departure? everything in Clojure is just data, and for good reason. the entire core lib is built around manipulating data. it's the best part of clj. - it's still place oriented, at least from what i gather so far 2. Better programmatic manipulation of specs. great! but again: why not make it data? then programmatic manipulation comes with. Imagine if the schema for:

{:weather {:weatherbitio {:key "xoxb" :default {:zip "98104"}}}
   :command {:prefix "!"}}
Was simply:
{:weather {:weatherbitio {:key string? :default {:zip string?}}}
   :command {:prefix string?}}
There's elegance in mirroring the data structure you're specifying, kinda like how Datomic Pull queries mirror the data you get get back.

schmee20:01:59

the answers to most questions about “why doesn’t spec do X” are on that page

schmee20:01:52

you might be interested in this though: https://github.com/HealthSamurai/matcho

jstaab20:01:31

I came here to ask a very similar question. I do agree with the two points you linked above, but it does seem very strange to use static place orientation to achieve it. I'd like to programmatically generate specs based on my own domain modeling dsl, rather than repeat my entity shapes multiple times, use spec as my domain modeling language, or write complex macros to do both at once. I've heard the objection of "why is it so hard to generate composed specs programmatically", and I still haven't heard a good answer.

schmee20:01:19

they’re working on a new version of spec which is supposedly more data-oriented, there are some details about the development here, but nothing is released publicly yet: http://insideclojure.org/2019/01/11/journal/

jstaab20:01:33

I've heard of that, I have high hopes 🙂

ikitommi06:01:46

@devth spec-tools has a data-layer on top of specs like you described:

(require '[spec-tools.data-spec :as ds])
(require '[clojure.spec.alpha :as s])

(def weather-spec
  (ds/spec
    {:spec
     {:weather {:weatherbitio {:key string? :default {:zip string?}}}
      :command {:prefix string?}}
     :name ::weather}))

(s/valid? 
  weather-spec
  {:weather {:weatherbitio {:key "xoxb" :default {:zip "98104"}}}
   :command {:prefix "!"}})
; true
just functions & data.

ikitommi06:01:48

the forms are mapped with a multimethod, so string? gets a 'clojure.core/string? form. doesn’t help with anonymous functions.

devth15:01:31

looks good! i'm planning to use it

Dustin Getz20:01:14

but string? is code, not data

Dustin Getz20:01:37

the idea of predicates is core to spec, i dont see how spec could ever be jsut data

devth20:01:01

data for edges, predicates for leaf nodes

Alex Miller (Clojure team)20:01:23

the symbol string? is data

Alex Miller (Clojure team)20:01:39

the form (s/coll-of string?) is also data

Dustin Getz20:01:39

that’s thin

Alex Miller (Clojure team)20:01:46

it’s not thin, this is the whole basis of lisp

Dustin Getz21:01:14

homoiconicity is about macros

jstaab21:01:45

If you call describe it does return a list of symbols at runtime, so spec isn't just confined to macros. Admittedly it could be hard to do anything with more complex predicates

Alex Miller (Clojure team)21:01:26

that’s just … not true

Dustin Getz21:01:09

for string? to have meaning we’d have to syntax quote it for starters, and now we’re in the clojure reader, not the edn reader

Alex Miller (Clojure team)21:01:10

and spec forms will be even more central in spec 2

Alex Miller (Clojure team)21:01:14

string? has meaning in the context of a namespace

Dustin Getz21:01:37

so now we hve (ns) forms and are very solidly in the clojure reader, not edn reader

Dustin Getz21:01:14

that’s how i udnerstand it anyway, always looking to deepen 🙂

Alex Miller (Clojure team)21:01:26

if you have fully qualified symbols, then the edn reader is sufficient

Alex Miller (Clojure team)21:01:43

for example, s/form always give you that

Alex Miller (Clojure team)21:01:31

we are also moving towards a world where spec forms are a data form, but may not be the only data form

Alex Miller (Clojure team)21:01:48

so some “map” data form could also exist

Alex Miller (Clojure team)21:01:37

but that’s not more or less data than the list form (it may be easier to perform some kinds of transformations on)

10
Dustin Getz21:01:08

yo’re saying (s/coll-of ...) is sugar for something deeper?

Alex Miller (Clojure team)21:01:09

I’m saying that (s/coll-of string?) is data. Some map syntax {:op coll-of :pred string?} (not a real syntax) is also data. both are sufficient to describe a spec.

favila21:01:50

I don't think this matters. the power and problem is the predicates. predicates can only be data if you know what all of them mean (independently of an implementation--a well-known list of predicates)

Alex Miller (Clojure team)21:01:53

yes, that is essential in the design of spec which from day 1 was predicate based

favila21:01:07

and that is what people mean when they say "specs are not data"

Dustin Getz21:01:15

i accept that a qualified symbol is equivalent to a qualified keyword – but not a closure or fn reference, only symbols

favila21:01:37

> the idea of predicates is core to spec, i dont see how spec could ever be jsut data

favila21:01:48

that's I think what you mean @dustingetz?

favila21:01:58

certainly how I understood it

favila21:01:07

in my own thinking about the "dataness" of spec

Dustin Getz21:01:08

As soon as someone puts #(and x y) as a spec pred, it’s all over, no logner data

Alex Miller (Clojure team)21:01:32

well, that’s false and we support that now

Dustin Getz21:01:39

how can you serialize that

Dustin Getz21:01:59

its a reference

Dustin Getz21:01:02

it could be jvm interop there

Alex Miller (Clojure team)21:01:06

or you mean x and y as closed over values?

Dustin Getz21:01:22

right, it could be anything

Alex Miller (Clojure team)21:01:24

although we have some thoughts on how to allow you to do that as well

Dustin Getz21:01:36

now that has my interest piqued …

Alex Miller (Clojure team)21:01:56

if x and y are vars you’re invoking

Alex Miller (Clojure team)21:01:27

if it’s values, then you’re in the realm of custom spec creation (which also is now more codified and easier to do)

Alex Miller (Clojure team)21:01:49

well, maybe not that much easier, but I think we’re ready to commit to what that means

Alex Miller (Clojure team)21:01:20

I would still say that pred references are data if it’s possible to re-establish their meaning remotely (in the context of namespaces) and var serialization / hydration is something that we’ve been slowly working on for the last year or so. some of it’s in clojure already.

Dustin Getz21:01:55

Aren’t you going to run up against the halting problem

(= #(even? (inc (inc %)))
   #(even? (+ 2 %)))

Alex Miller (Clojure team)21:01:04

we may ultimately end up going beyond var quote to define something new, but we’re still just thinking about those parts

Alex Miller (Clojure team)21:01:21

why do you need to do that?

Alex Miller (Clojure team)21:01:31

we’re never comparing specs for equality

Dustin Getz21:01:58

because you said it’s data, so i want to do data things, otherwise doesn’t that defeat the point

Alex Miller (Clojure team)21:01:25

this is too theoretical. what problem are you trying to solve?

Dustin Getz21:01:27

(= `string? `string?)
is well defined since they are data and qualified

Alex Miller (Clojure team)21:01:45

why are you even doing that?

Dustin Getz21:01:10

Well for example if specs are data then i want to store them efficiently in a database

Alex Miller (Clojure team)21:01:23

you’re inventing problems that imho don’t exist

Dustin Getz21:01:10

it is essential to hyperfiddle that datomic schema is actually data and stored in a database

Dustin Getz21:01:53

we match on :db.type/string, for example, to decide what widget to draw

Alex Miller (Clojure team)21:01:07

well you’ll be glad we’ve scoped it to just data then - symbols, sets, and forms combining them

favila21:01:38

inline fns are no longer allowed?

Dustin Getz21:01:49

well, looking forward to seeing whatever you come up with

Alex Miller (Clojure team)21:01:23

work proceeding in https://github.com/clojure/spec-alpha2 although I have not pushed up in quite a while

Alex Miller (Clojure team)21:01:47

debating how bad it would be to push wip stuff there, but maybe I’ll just put it on a branch

favila21:01:31

spec is data if you have eval and a full clojure runtime and possibly other required nses got it

Dustin Getz21:01:48

Like for example, imagine a big honking match, like this but huge, and it tested the specs too. If specs are data that should be possible and could be very interesting.

Alex Miller (Clojure team)21:01:54

well specs are an open protocol, so no match is big enough to cover custom extensions

Dustin Getz21:01:11

could be multimethod, you get the idea

Alex Miller (Clojure team)21:01:39

specs have a form, keyed by the op in first position (and that’s how resolution occurs in the spec 2)

misha21:01:50

what is the difference between form and data?

favila21:01:13

data can be understood without evaluation

favila21:01:37

that can be true for a well-defined subset of spec

Alex Miller (Clojure team)21:01:41

as we’re talking forms above, I mean combinations of lists, symbols, keywords, and sets

misha21:01:18

> data can be understood without evaluation well, it depends, because your brain is the evaluator which "understands", and knowing format is essentially :require in brain (if POV is a person)

Alex Miller (Clojure team)21:01:47

it depends what you mean by “understood”

misha21:01:06

arguably, string? can also be understood w/o evaluation, even w/o namespace. so yeah, it depends on definition of "understood"

favila21:01:03

yes, most derivative uses of spec rely on some implementation-independent meaning for the predicate symbols

favila21:01:47

but you need to limit that set, or else have some way to mark useful traits about a predicate; otherwise you can't "understand" it without getting a clojure runtime to run it

misha21:01:59

more specific you (want to) get – more it'll depend on actual implementation behind the symbols

favila21:01:42

that's what I mean by a well defined subset

misha21:01:02

it starts to sound like static types argument. There is value in being able to use anything clojure allows to construct a predicate. But if you want it to be understandable over the wire – use subset of what is allowed. Instead of having spec forbid e.g. java interop, because it would not be understandable in 0.0000001% cases

Alex Miller (Clojure team)21:01:12

if you want use specs with the spec api calls (valid?, conform, explain, etc), then yes, you need implementations for all predicates mentioned in the specs

Alex Miller (Clojure team)21:01:42

this has been explicit in the design of spec from the beginning

Alex Miller (Clojure team)21:01:00

none of this is new or really any different

favila21:01:06

no one is arguing it's not been explicit, or even that it is bad or wrong

favila21:01:16

it's just annoying to hear that called "data"

Alex Miller (Clojure team)21:01:23

that’s baffling to me

Alex Miller (Clojure team)21:01:29

as this is no different than the whole “code as data” / macros thing that is part of Clojure, which presumably you are also familiar with

misha21:01:38

what is "data" to you? @favila

Dustin Getz21:01:41

data makes possible a whole new universe of systems in 20 years that we barely even have the words to talk about today (thanks to Clojure) – what seems pedantic today is pretty important in hindsight, i think

Alex Miller (Clojure team)21:01:35

just because parts of the data are linked to semantics, does not mean it’s not data

Dustin Getz21:01:25

[:find ?e :where [?e :dustingetz.reg/email]]
(d/q '[:find ?e :where [?e :dustingetz.reg/email]])
One of these is data, one of these is code (and code is data), but there is a huge difference in terms of what it is coupled to and what it means

Dustin Getz21:01:37

One of these can be used to generate UI or compile to SQL, the other can’t

misha21:01:17

both is edn data, no? vectors, lists, keywords, symbols

Alex Miller (Clojure team)21:01:40

well this has been fun, but I’m going to bow out here and go work on the code

❤️ 5
👍 5
lilactown21:01:33

I think that there is a valid desire to have specs be represented in a form that doesn't require a Clojure interpreter in order to use

lilactown21:01:12

we'd probably prefer to have a simpler DSL, using a subset of EDN, that would enable easier parsing and consuming

misha21:01:19

for example, how do you know what ?e is in 1st line? you need to provide "this is variable placeholder to be bound because datomic" context somehow somewhere. if you don't - this is exactly the same as 2nd line, but in a list, not vector

Dustin Getz21:01:49

Once it is coupled to Clojure, it is an order of magnitude harder to build a data driven system on top of it

Dustin Getz21:01:04

XML is data too

lilactown21:01:33

the trick is to tease out, what is the minimum set of semantics required to express a spec?

lilactown21:01:01

we can also turn the knob on that by limiting the ease and power of specs

lilactown21:01:39

e.g. right now, specs use predicates that you can easily inline. what if we disallowed that (either by convention or some other tricky mechanism)?

lilactown21:01:59

that would make it easier to serialize, but potentially less powerful and harder to write

lilactown21:01:11

"every predicate must have a qualified name"

Dustin Getz21:01:29

Or just say “hey this thing is programmable, have at it”, that’s fine too, but the halfway world is pretty weird and i think leads to fragility

devth21:01:16

wonder if free monads and the interpreter pattern is useful to consider in this context. spec as a pure data AST.

misha21:01:19

(defn make-sql [data] data)
=> #'user/make-sql

(-> "[:find ?e :where [?e :dustingetz.reg/email]]"  (edn/read-string)  (make-sql))
=> [:find ?e :where [?e :dustingetz.reg/email]]

(-> "(d/q '[:find ?e :where [?e :dustingetz.reg/email]])"  (edn/read-string)  (nth 2)   (make-sql))
=> [:find ?e :where [?e :dustingetz.reg/email]]

lilactown21:01:28

or we could have a convention that says, any predicate must not close over any vars

misha21:01:52

> One of these can be used to generate UI or compile to SQL, the other can’t see above

Dustin Getz21:01:01

(d/q (->> [:find '?e] (concat [:where ['?e :dustingetz.reg/email]]))) i can play that game all day

misha21:01:09

it has extra step, yes, but it does not make it not data

misha21:01:44

well, how do you know the 1st one can be converted to sql?

misha21:01:05

by looking at it first? you interpret while you look. your brain is repl

misha21:01:40

brain of someone w/o knowing what edn is will not be able to tell that 1st can be converted and second cant

misha21:01:57

it's a repl w/o edn/reader required yet

favila21:01:23

I don't think data = edn

misha21:01:39

yes, it is less convenient, and requires extra step. if this is the argument - I agree, there is no limit to convenience opieop

favila21:01:58

if data is just "anything you can quote" then what is code?

favila21:01:44

when people say "this is data" they are saying it has a constellation of useful properties that are strictly less powerful than a programming language

misha21:01:23

what does it mean? in this spec conversation context, and ... in general?

favila21:01:49

e.g., being able to understand and interpret it multiple ways; draw inferences from it that are not stated explicitly; compare it to other things for equivalence; build different interpreters on top of it

misha21:01:25

you somehow need to parse/read/interpret data to "draw inferences"

lilactown21:01:27

I'm having flashbacks to this conversation between Rich Hickey and Alan Kay a few years ago: https://news.ycombinator.com/item?id=11945722

favila21:01:48

yes @misha, you do. You need to assign meaning to things without implementing them

favila21:01:57

this is possible for spec until you hit a predicate

misha21:01:11

otherwise how do you know what is in the string? how do you know that this is map: {:a 1}, etc.

favila21:01:16

if you have a well-known list of predicate symbols, then you can hard-code that meaning in

misha21:01:18

you have to have some context, it might be in your brain, but you need to communicate it to the system "which might not be using clojure, etc."

favila21:01:58

but once you have a custom predicate, it's meaning cannot be shared without sharing it as code

lilactown21:01:03

I don't think it even needs to be well-known

favila21:01:11

there's no data language that can express a new predicate

favila21:01:27

spec has parts of its language that can express new specs

Dustin Getz21:01:27

Data is also secure, imagine a REST service that returned Clojure code

lilactown21:01:31

you could receive a spec, and then read all of the symbols from that spec to see what needs to be implemented

favila21:01:33

e.g. s/or s/and etc

favila21:01:55

but below predicates, you cannot know anything unless you have independent knowledge of its meaning

misha21:01:15

I think, that "assigning meaning w/o implementation" is a very blurry line

favila21:01:18

even executing it you can only know what it means for a specific value you give to it

misha21:01:32

hardcode that meaning where?

favila21:01:34

you cannot make any universal inferences about the behavior of a predicate without knowing its meaning

misha21:01:48

because that is just "global state", and you need to make sure system you are sending it to - is in the same context

Dustin Getz22:01:14

Datomic’s :db.type/string is not ambiguous or blurry

favila22:01:50

@misha having to send it to the same runtime environment to use it sounds an awful lot like a property of code rather than data

misha22:01:00

show it to java dev and it would be pretty ambiguous to him opieop

Dustin Getz22:01:34

The success of HTML over desktop gui toolkits is also because it is data

Dustin Getz22:01:59

You can ship HTML over the wire to arbitrary platform, and somehow they manage to interpret it usefully

misha22:01:05

well, then yours "well defined symbols we could just hardcode" just means "we have implementation :ruquired here in clojure and there in python, so dont worry about it"

misha22:01:21

which smells like transit

misha22:01:38

otherwise, define "well defined" :)

Dustin Getz22:01:41

You can render arbitrary HTML fearlessly, you aren’t worried about getting hacked

favila22:01:50

"well defined" just means we know the list ahead of time

favila22:01:09

I know what "clojure.core/string?" means

favila22:01:19

I don't know what "foo.core/blah?" means

favila22:01:44

If I had the code I could possibly execute foo.core/blah?

misha22:01:46

html just has very small set of tokens

Dustin Getz22:01:58

You do however know what github/markdown? is, you know who the maintainer is, you know where the docs are and where you might find useful code to operate with it

favila22:01:26

maybe another angle: spec can express things that are not in spec

favila22:01:40

i.e. the predicates

misha22:01:08

well, html makes no sense to my mom, neither does english, so can I say it is not data then? ¯\(ツ)

Dustin Getz22:01:30

Yet somehow I manage to pay my bills

misha22:01:49

her brain is a system, and it does not support html. mine does.

misha22:01:45

@favila you know the list of clojure code ahead of time too. it is just way too large.

misha22:01:17

the same way we know the set of real numbers

favila22:01:42

will foo.core/blah? return true for a real number?

misha22:01:00

one difference would be - you cant eyeball it and say "yep, it's fine, we are not getting hacked"

misha22:01:34

will the next system interpret 1 as true or int?

lilactown22:01:35

sometimes you have to reject invalid data if you don't know how to parse it, too

favila22:01:03

or you convert it to a less meaningful form, e.g. clojure tagged-value

favila22:01:20

or, a fully qualified predicate symbol

favila22:01:53

but then the power the spec gained by being able to use any predicate it wants is lost by any other system that wants to make sense of the spec

favila22:01:50

by making s/valid? easier something else has been made harder

misha22:01:57

@dustingetz if stuff on github - is data, code is data in exactly same way, so we are on the square 1 again

lilactown22:01:10

this is going to sound :male-astronaut: but: it becomes just like any other part of our system, where we need to keep a single source of truth for the semantics of that data

lilactown22:01:26

e.g. maybe in the future we'll be building "spec-services"

lilactown22:01:35

(I'm thinking of building a "spec-service")

favila22:01:42

that just sounds hard to do given predicates

Dustin Getz22:01:08

@lilactown What is a spec service?

lilactown22:01:36

well, because serializing the spec itself requires a lot of context, what I'm thinking of doing is just having, literally, spec-as-a-service

Dustin Getz22:01:55

so like an xmlns

lilactown22:01:14

GET valid/foo.core/blah => (s/valid? :foo.core/blah (:body req))

lilactown22:01:42

for instance, we have a lot of disparate services that need to validate similar sets of data according to the same rules

lilactown22:01:54

and they're not all Clojure 😞

Dustin Getz22:01:51

Yes, e.g. imagine internet protocols like HTTP had a formal spec written instead of English, in clojure.spec, and thus it becomes much easier to, well im not sure what i would want to think about it

jaihindhreddy-duplicate22:01:25

I was just about to point out hyperfiddle. Then I read your name 🙂

Dustin Getz22:01:14

It is a bit like HATEOAS

Dustin Getz22:01:38

in fact it is HATEOAS

❤️ 5
Dustin Getz22:01:04

I retract that, but it is really interesting to imagine

Dustin Getz22:01:53

If spec is clojure code, it gets a little harder to start throwing them around on wires though, because how can you trust if it is safe to clojure.core/eval

Dustin Getz22:01:07

But there is still a trust chain, so its probably fine

Dustin Getz22:01:32

But you’re saying a service, so that bypasses the trust issue

lilactown22:01:49

right. just like in clojure, you have a single, global spec registry

lilactown22:01:28

your system should have the same: a single, global registry of what your specs are

favila22:01:51

yeah that's a neat idea

favila22:01:15

pretty scary to run that server

favila22:01:35

hopefully no one manages to get a bitcoin mining predicate on there

💀 5
favila22:01:55

how to make it fast?

favila22:01:06

that's a lot of data to push over the wire

favila22:01:19

(uploads of clojure data to s/valid?)

Dustin Getz22:01:32

Right, the huge difference between Datomic schema and Clojure specs today, is that you can recklessly throw them around. For example it’s easier to deploy them into a validated healthcare enterprise environment

Dustin Getz22:01:43

Slurp in some EDN updates, not that big a deal

Dustin Getz22:01:54

Slurp in some clojure specs to pass to eval – yeah right

Dustin Getz22:01:17

I can imagine a dialect of clojure/core that does not have infinite recursion, and no unsafe operators like !, that might be safe enough

Dustin Getz22:01:46

You can also make the requestor pay for compute

lilactown22:01:51

yeah, easier to tag things with specs and send the data you want validated along with the names of specs you want it to be validated against

Dustin Getz23:01:12

Here is a cool application of specs as data: [:select.ui (select-keys props [:value :class :style :on-change :read-only :on-click])] aka https://github.com/facebook/prop-types

misha23:01:42

it funny how

// An object that could be one of many types
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]), ...
is data, and
(d/q '[:find ?e :where [?e :dustingetz.reg/email]])
is not d