Fork me on GitHub
#clojure-spec
<
2017-09-22
>
jeaye00:09:53

Given a fn like (defn foobar ([a] (inc a)) ([a b] (str a b))) where arity 1 expects a number and, let's say, arity 2 expects two strings, can I spec this so that arity 1 doesn't accept a single string?

jeaye00:09:22

Everywhere I see people spec'ing multiple arities, they'll just use s/or. That leaves all sorts of open holes.

jeaye00:09:42

I'd see something like this: (s/fdef foobar :args (s/cat :a (s/or :int integer? :str string?) :b (s/? integer?)))

jeaye00:09:52

But that permits (foobar "bad")

seancorfield00:09:14

(s/fdef foobar :args (s/or :one (s/cat :a number?) :two (s/cat :a string? :b string?)))

jeaye00:09:25

Damnit, that worked perfectly. Cheers, @seancorfield. I think I had tried s/alt there, with two s/cats, but it failed.

seancorfield00:09:58

s/alt will alternate multiple sequence regexes <-- this is poorly worded but I'm not quite sure how to describe its interaction with s/cat

jeaye00:09:39

This would be a good thing to have in the spec guide, I think; multiple arities aren't covered at all.

seancorfield00:09:04

It probably doesn't help that the docstrings for s/alt and s/or look so similar. Seems to be a common point of confusion.

jeaye00:09:13

Yep, agreed.

seancorfield00:09:28

I just tried it with s/alt in place of s/or and it worked -- but the spec failure you get for (foobar "a") is different...

boot.user=> (foobar "a")

clojure.lang.ExceptionInfo: Call to #'boot.user/foobar did not conform to spec:
                            In: [0] val: "a" fails at: [:args :one :a] predicate: number?
                            val: () fails at: [:args :two :b] predicate: string?,  Insufficient input
                            :clojure.spec.alpha/spec  #object[clojure.spec.alpha$or_spec_impl$reify__810 0x55365ffb "[email protected]"]
                            :clojure.spec.alpha/value  ("a")
                            :clojure.spec.alpha/args  ("a")
                            :clojure.spec.alpha/failure  :instrument
                            :clojure.spec.test.alpha/caller  {:file nil, :line 1, :var-scope boot.user/eval1830}
compared to
boot.user=> (foobar "a")

clojure.lang.ExceptionInfo: Call to #'boot.user/foobar did not conform to spec:
                            val: () fails at: [:args] predicate: (alt :two (cat :a string? :b string?)),  Insufficient input
                            :clojure.spec.alpha/spec  #object[clojure.spec.alpha$regex_spec_impl$reify__1200 0x373ea576 "[email protected]"]
                            :clojure.spec.alpha/value  ("a")
                            :clojure.spec.alpha/args  ("a")
                            :clojure.spec.alpha/failure  :instrument
                            :clojure.spec.test.alpha/caller  {:file nil, :line 1, :var-scope boot.user/eval1846}

seancorfield00:09:38

In the first case, it fails to match :args :one :a because "a" is not a number? and it also fails to match :args :two :b because only one string was provided.

seancorfield00:09:40

In the second case, it only reports the failure to match on alt :two (again, because only one string was provided).

seancorfield00:09:58

The spec I used was (s/fdef foobar :args (s/alt :one (s/cat :a number?) :two (s/cat :a string? :b string?)))

jeaye01:09:32

Ah, I was getting the second one, but I was also likely testing with invalid inputs and ... bah, I don't remember. Awesome to see it works well; thanks again.

jeaye01:09:18

Not having to calculate the various alternations between arities for use with s/or is going to make my macro writing significantly simpler as well.

jeaye05:09:19

Is there a good spec floating around for specs themselves? They can be ifn? but also things like s/coll-of return something else. I've also tried s/spec?.

Alex Miller (Clojure team)11:09:39

Currently spec instances should just be considered to be opaque and respond to true for s/spec?

jeaye17:09:11

Awesome, thanks. Good to see there's work being done here.

seancorfield05:09:36

That would be a good question for Alex when he's around tomorrow @jeaye

jeaye07:09:13

Alright, I'll bug him in the morning.

dbushenko08:09:55

will Alex be today?

vikeri09:09:02

I can’t seem to get my mutimethods to become intstrumented. Anyone else also having this issue?

rovanion09:09:17

Multimethods are weird, one of those things I sometimes restart my repl for and it just works.

andrewmcveigh09:09:56

@vikeri see above (hope the link works)

richiardiandrea18:09:14

@gfredericks is it possible to write a generator that continues to generate until the sum of an operation on the generated stuff meets a condition?

richiardiandrea18:09:37

for instance generate integers until their sum is 5

tbaldridge18:09:24

@richiardiandrea that doesn't make a lot of sense, since there's a almost unlimited set of numbers that would sum up to 5

gfredericks18:09:41

you want a collection of integers in particular?

richiardiandrea18:09:45

right, let's say I restrict the domain

gfredericks18:09:05

and you want the sum to be =5, or <=5?

richiardiandrea23:09:50

I see now the reason of this question, I am basically now generating a list of things such that their sum <= total randomly, if < then the last element is manually crafted

gfredericks01:09:06

yeah, that's definitely a legit way to do it

richiardiandrea18:09:30

basically I would want to have a such-that, but where I accumulate results first, then I apply pred on the result

gfredericks18:09:55

one idea is to generate larger sequences and fmap it to the largest prefix that meets your criteria

gfredericks18:09:57

would that be sufficient?

richiardiandrea18:09:22

uhm, let me try that, I will be back with the result 😄