Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add schpec.number #1

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open

Add schpec.number #1

wants to merge 4 commits into from

Conversation

dball
Copy link

@dball dball commented Jul 20, 2016

Note if you're really gonna take this ball and run with it - which is awesome - a shorter ns might be nice. Personally, I'd be cool with schpec as the top level.

Curious what testing approaches you think might be good. Examples and clojure.spec.test exercises?


(s/def ::real
(s/spec real?
:gen #(gen/double* {:infinite? false :NaN? false})))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this spec/pred intended to apply just to doubles? If so, I would use a name closer to "double" or "floating-point", and check that it's an instance of Double in the predicate; I personally don't like the name "real" for numeric types because of the mismatch with the mathematical term.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's intended to apply to all real number values, which as far as I can reason, include all things clojure considers numbers except these three corner cases. I confess, I don't see any mismatch between this predicate and the mathematical domain of real numbers. Is it because there are irrational numbers (e.g. pi) that don't have precise representations?

Clojure core already provides double?. My intent here is to provide predicates and specs for numbers in terms of their domain (e.g. real numbers, numbers that can be precisely represented as decimals with such and such precision), not their actual types.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think about the name finite? for this?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, irrational numbers are the sort of thing I'm thinking of w.r.t. the word "real"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll mull over finite?. Note of course there are plenty of real, irrational numbers that do pass this predicate: (/ 1 3) and friends. It's not pi's fault that clojure doesn't have an e.g. Taylor series expansion type to compute it to arbitrary precisions :)

If the predicate failed to cover such a case, I'd buy the argument that it's not named correctly. But AFAICT, it covers all real numbers expressible in clojure's arithmetic.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand this comment fully; (/ 1 3) looks like a rational.

@gfredericks
Copy link
Owner

example tests would definitely be good here; does "clojure.spec.test exercises" refer to just running the generator a bunch to make sure it doesn't crash?

@gfredericks
Copy link
Owner

thanks for the PR by the way; I intend to keep the namespace the same as I appreciate the convention of following DNS naming so we don't have to worry about our libraries colliding.

In fact I'm tempted to ask that you move this down to com.gfredericks.schpec.specs.numbers; at the very least I like numbers better than number.

:max max})))))]
(s/spec pred :gen gen))))

(s/def :specs.number.decimal/precision
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're putting these in a different namespace to distinguish them from the user-facing specs?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, they're not intended as published specs for general use, they're just to support the s/keys :opt-un. I've tried a few different techniques for bespoke options specs and haven't really found anything I like better than this yet.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's interesting, and it raises the question of how users are going to be discovering available specs; if they just rely on our documentation, then this isn't a concern.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use bare keys here

(s/def ::plugin-item
  (s/and
   (s/cat :lib-name symbol?
          :version-str non-blank-string?
          :dependency-item-args
          (s/* (s/alt :optional      (s/cat :ky #{:optional}
                                            :val boolean?)
                      :scope         (s/cat :ky #{:scope}
                                            :val ::name-like)
                      :classifier    (s/cat :ky #{:classifier}
                                            :val ::name-like)
                      :native-prefix (s/cat :ky #{:native-prefix}
                                            :val (some-fn string? symbol? keyword?))
                      :extension     (s/cat :ky  #{:extension}
                                            :val (some-fn string? symbol? keyword?))
                      :exclusions    (s/cat :ky #{:exclusions}
                                            :val ::exclusions)
                      :plugins-args  (s/cat :ky #{:middleware :hooks}
                                            :val boolean?))))))

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems strange to register keys that are just argument keys, they are not global types or contracts...

@dball
Copy link
Author

dball commented Aug 7, 2016

@gfredericks I revised the semantics and names in response to the comments: namely, I renamed real to finite and switched decimal-in to bigdec-in and enforce the bigdec type. I can also change the ns if you would prefer.

Double/POSITIVE_INFINITY false
Double/NEGATIVE_INFINITY false
Double/NaN false
true)))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this case statement works; could you add tests?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely

@gfredericks
Copy link
Owner

yes, numbers instead of number would be nice, thanks.


(s/def :specs.number.bigdec/max
(s/and bigdec?
::finite))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer a naming convention for these specs that's actually scoped to com.gfredericks.schpec; I suppose a subnamespace would work best: :com.gfredericks.schpec.numbers.bigdec-in/max.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sorry, holdover from my personal project. It's fixed, and there's a
trivial example test for it.

Would we like to see clojure.spec.test/stest exercises in the tests?

On Sun, Aug 7, 2016 at 12:33 PM, Gary Fredericks [email protected]
wrote:

In src/com/gfredericks/schpec/number.clj
#1 (comment):

  •  (s/spec pred :gen gen))))
    
    +(s/def :specs.number.bigdec/precision
  • pos-int?)

+(s/def :specs.number.bigdec/scale

  • (s/spec (fn [x](and %28int? x%29 %28not %28neg? x%29%29))
  •      :gen #(gen/large-integer\* {:min 0})))
    
    +(s/def :specs.number.bigdec/min
  • (s/and bigdec?
  •     ::finite))
    
    +(s/def :specs.number.bigdec/max
  • (s/and bigdec?
  •     ::finite))
    

I'd prefer a naming convention for these specs that's actually scoped to
com.gfredericks.schpec; I suppose a subnamespace would work best:
:com.gfredericks.schpec.numbers.bigdec-in/max.


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/gfredericks/schpec/pull/1/files/9b4096f665b827eac7843e8fb77e41f9c3705d4c#r73805035,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAAx2zx1Z3X-p_6uICJ_YoTi9gpvjhVqks5qdgjmgaJpZM4JRIaK
.

@dball
Copy link
Author

dball commented Aug 7, 2016

There's that simple test. I can add more tests in a couple of hours, particularly using stest/check on bigdec-in, but I'm heading to the gym rn tho.


(s/def ::finite
(s/spec finite?
:gen #(gen/double* {:infinite? false :NaN? false})))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you intend this to be just Doubles or other numeric types as well? The generator implies the former while the predicate implies the latter.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. If we wanted the latter, we'd need to choose the generator from, what, longs, doubles, bigdecs, bigints? Would that be sufficiently broad, or would we want shorts and ints and floats also?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ratios too probably

I realized after our conversations a couple weeks ago that a big reason I don't see a lot of utility in specs that combine exact and inexact types is that normal equality doesn't work; e.g., (not= 42 42M) and (not= 42M 42.0). So I see the natural classes of numeric types as:

  • fixed-integer < arbitrary-integer < ratio(including integers)
  • bigdec
  • doubles

where any of those five are natural classes to spec out, but combining those three categories is rather less useful.

So all that to say, I think restricting this to doubles makes a lot more sense.

Of course this gets a lot more muddled when we consider clojurescript :/

@dball
Copy link
Author

dball commented Aug 7, 2016

I am into that, but now we're back to the question of names: what do we call finite?. Should it be finite-double??

I'd be into dropping it, except for the fact that in literally every production repo I've introduced spec to, I've written this spec.

@gfredericks
Copy link
Owner

finite-double? is good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants