Fork me on GitHub
#clojure-spec
<
2018-11-20
>
lwhorton00:11:51

is it possible to express “a map with either key A or key B, but not neither”?

lwhorton00:11:37

(without unioning the value of the key under something like s/keys :req [:a/foo] where s/def :a/foo (s/or :b :a/bar :baz :a/baz) (since i want the key’s value to be either :bar or :bar and not :foo)

lwhorton00:11:14

oh, lol. :req [(or :bar :baz)])?

roklenarcic08:11:48

Is there any difference between: (s/& (s/cat .... and (s/and (s/cat ...

Alex Miller (Clojure team)12:11:54

& is a regex op that nests with other regex ops

Cheng08:11:43

I read some spec impl code, I see it using a global store for these specs. I think spec is more of meta data of data, why it cannot implemented via meta data? Make it a meta system?

roklenarcic09:11:00

metadata of what?

roklenarcic09:11:25

When I define a spec for a keyword, where exactly would you attach the metadata?

schmee09:11:51

@roklenarcic s/& is for spec regexes while s/and is for general predicates

didibus09:11:21

Well, specs are not tied to a var, so meta wouldn't really make sense. They are tied to values. The keyword or symbol is just a name for the spec

schmee09:11:01

fspecs are tied to vars

didibus09:11:22

No they're not

schmee09:11:24

or, it can be at least 🙂

didibus09:11:46

Well, you can relate them

didibus09:11:26

That's why check-fn takes a function and a spec seperate. Because you could use any fspec to spec to check any function

schmee09:11:10

yeah, I was thinking of s/fdef

didibus09:11:19

Ya, I mean, in practice, for functions, its convenient to use the same symbol to point to your function and its spec.

didibus09:11:42

So in that case, you could argue Specs could have just been meta on the function

didibus09:11:53

Or its var

didibus09:11:05

But, when your speccing data, that wouldn't have made sense

didibus09:11:32

So that's why a registry is more logical

didibus09:11:04

Also, now you can define specs before you define functions.

Cheng09:11:03

@schmee i am in china, now i cannot open that url, i will check that article later, thanks for the information.

Cheng09:11:34

i think spec is meta data in essence, whatever it currently implemented via meta mechanical or not.

Cheng09:11:21

i think the problem is keyword should support meta data as we are associating meta data to it.

Alex Miller (Clojure team)12:11:07

Keywords are interned and reused so cant support metadata

Cheng09:11:44

i think now it’s a workaround to impl meta data in global store, is is really a good practice to do so?

didibus09:11:01

It depends what the meta is about. Like if you want to specify additional things about a Var then you can add that to the Var meta. Same for anything that supports meta.

didibus09:11:06

But Spec is meta about values

didibus09:11:24

And values can not have meta attached to them directly. A value can be an int for example

didibus09:11:38

So for example, this is a spec #(> % 10)

didibus09:11:29

It doesn't need to be attached to anything to be one

didibus09:11:03

So say you want to give that spec a name, you can do s/def ::greater-then-ten #(> % 10)

didibus09:11:41

You did not add meta to the keyword ::greater-then-ten here. This spec is not metadata about the keyword.

didibus09:11:18

It is just a specification of values that you named ::greater-then-ten

didibus09:11:55

Now you can do: s/valid? ::greater-then-ten 15) for example

didibus09:11:33

So I'm not sure you should think of Specs as meta data. It isn't really meta data in the normal sense. Like, yes, in this case, you can say that 15 conforms to the ::greater-then-ten spec. And so, ::greater-then-ten can be seen as metadata about the value 15. But again, meta about values is not really a concrete thing.

Cheng10:11:45

@didibus thx for the explanation

Cheng10:11:34

in my opinion there are plenty of problems about current spec impl, for example: global store for spec, use keyword as spec name, mix keyword with spec, instrument is a state changer...

Cheng10:11:07

overall, it’s not FP style

Cheng10:11:28

it’s OO style

mpenet10:11:16

for sure there are issues but it's not related to OO or fp at all

mpenet10:11:41

vars are "global", shoving everything in them would be the same ultimately, you just move the bloat somewhere else. A separate registry is a good thing

mpenet10:11:33

ask youself: where is the info about the Types "stored" (if that even makes sense) in haskell: it's not a first class thing

borkdude10:11:55

even in Haskell programs they use global mutable cells, IORef, MVar, TVar, etc.

mpenet10:11:50

spec-alpha2 is supposed to fix quite a bit of the common complains, so I guess "wait and see"

didibus10:11:22

• The global store has advantages too. It is simpler to manage a single global store, and namespaces should avoid clashes. • You don't have to use keywords for spec names, you can also use symbols. But I think keywords was chosen as convention specifically to make it visually separate from Vars

didibus10:11:35

* Instrument is used for testing only. And, it shouldn't be altering state. Think of it as aspect oriented programming if you know about that.

Cheng10:11:42

instrument directly changed the function via byte code operate, i hope instrument can return a new function not directly change on existing function

Cheng10:11:39

i didn’t aware we can use symbol instead of keyword in a/def

borkdude10:11:08

instrument doesn’t change the original function, it wraps it in a new function that’s stored in the root of the var

borkdude10:11:20

on unstrument this is reverted

Cheng10:11:23

i don’t see it’s more easy to maintain a global store than separate vars

borkdude10:11:46

an alternative could maybe have been via metadata on the var: instrumented? true but that would incur performance overhead when not using spec, because when calling a function via a var you would have to do this check

didibus10:11:29

Ya, instrument and unstrument also take a lock on the Var before changing it

didibus10:11:18

But it does mean you can not have an instrumented fn in one thread, and an uninstrumented one in another. Though that almost sounds like a positive in this case.

borkdude10:11:22

you can have with-instrument-disabled per thread maybe, since it’s a dynamic var?

didibus10:11:38

Oh, that's true, I just saw the instrumented code checks for a dynamic var to decide to validate or not. So I guess you could in fact do that.

didibus10:11:12

Hum, that gives me an idea for when I alter-var-root things in my tests. I could instrument things to be "mockable", and then have a dynamic binding pointing to a mock, and this different mocks per thread and my tests would work in parallel.

didibus10:11:38

@xfcjscn909 If you wanted non global stores, you'd have to first create a spec registry and put it somewhere in scope. And if it wasn't in scope anymore, it would stop working. And then as soon as you have more than one, you might forget which registry holds the spec you are looking for. All spec functions would take an additional arg for the registry to use. I mean, it would not be terrible, but I feel it would be a bit more inconvenient. And in practice, I never felt the need for separate registry. That's what namesapces are for on symbols and keywords.

didibus10:11:32

In effect, you would be using a Var as a namespace mechanism, instead of just using the existing namespace mechanism of keyword and symbols I feel.

borkdude10:11:36

Clojure apps pretty much converged to “one global atom” instead of “many smaller atoms”, e.g. re-frame

borkdude10:11:40

it’s much easier to reason about

didibus10:11:06

Also I'd say, if you don't find that top level defs are a problem for defining Vars, in practice, Specs are defined in a similar fashion, so it'll be a similar experience.

borkdude10:11:53

the thing that would maybe problematic is if multiple libraries define specs for the same thing. e.g. lib1 defines a spec for clojure.core/merge and lib2 does too. currently you have no way to say: I want to exclude loading this fdef when requiring a namespace

borkdude10:11:19

last one wins

didibus11:11:36

Hum, right. I think I tend to use Spec much more around my data, and a lot less to spec functions. So I haven't faced these kind of issues very much.

didibus11:11:44

Not yet at least

borkdude11:11:54

it hasn’t been a major problem, but it would be useful to me to see from which namespace a spec comes from and to conditionally instrument depending on the namespace where it came from.

borkdude11:11:32

e.g. here I have some code only instrument/unstrument specs from speculative: https://github.com/slipset/speculative/blob/master/src/speculative/instrument.cljc but if the “where did my spec come from” feature existed, I didn’t have to

didibus11:11:35

Hum, interesting. fdef could probably capture that

didibus11:11:07

Probably was not a feature they thought of

borkdude11:11:11

yeah and it could store it in the registry

borkdude11:11:47

but maybe in a future version of spec specs will have some form of metadata, so it could also happen there

didibus11:11:06

Ya, meta on spec would be great.

Alex Miller (Clojure team)12:11:55

It’s on the list

👍 12
martinklepsch15:11:37

Is there a way to define a spec that matches the structure of a conformed value?

martinklepsch15:11:02

E.g. (s/cat :a string? :b string?) would conform to {:a "a" :b "b"}. Now I want to instrument a function so that it only takes values having :a and :b with values matching string?

martinklepsch16:11:00

ok cool, thanks. Is the general expectation that once values are conformed they are no longer passed to functions that have spec'ed arguments? (I guess in most cases conformed values won't match the original spec)

Dormo22:11:11

Is there a way to make a spec to check if a vector has either 1 or 2 values, where the first value is a keyword and the second value is "anything or nothing"? I was thinking this would work, but it does not: (s/def ::event (s/tuple keyword? (s/nilable any?)))

Alex Miller (Clojure team)22:11:04

(s/def ::event (s/cat :k keyword? :a (s/? any?)))

👍 8
Dormo22:11:17

Perfect! Thanks!

Dormo23:11:02

The explicit key names are actually a bonus as well.

Alex Miller (Clojure team)23:11:11

the regex ops cat, alt, +, *, ?, etc are regex ops used to describe the structure of a sequential collection