-
Notifications
You must be signed in to change notification settings - Fork 204
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 :float
support
#1055
base: master
Are you sure you want to change the base?
Add :float
support
#1055
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Self-review comments 📖
src/malli/generator.cljc
Outdated
(defmethod -schema-generator :float [schema options] | ||
(let [max-float #?(:clj Float/MAX_VALUE :cljs (.-MAX_VALUE js/Number)) | ||
min-float #?(:clj Float/MIN_VALUE :cljs (.-MIN_VALUE js/Number)) | ||
ceil-max (fn [v] (if (nil? v) max-float (min max-float v))) | ||
floor-min (fn [v] (if (nil? v) min-float (max min-float v))) | ||
props (m/properties schema options) | ||
min-max-props (-min-max schema options) | ||
infinite? (get props :gen/infinite? false) | ||
NaN? (get props :gen/NaN? false)] | ||
(gen/double* (merge {:infinite? infinite? | ||
:NaN? NaN?} | ||
(when (or (not infinite?) NaN?) | ||
{:min (floor-min (:min min-max-props)) | ||
:max (ceil-max (:max min-max-props))}))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I was unsure how to exactly generate a float or a instance of Float
, but I attempted to constrain the generated values to something that could be coerced into a Float
instance.
If someone knows how/where to coerce the generated value into a Float instance then please let me know 🙏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call (gen/fmap float (gen/double* ...))
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although that doesn't work for ##Inf
and ##-Inf
. Might not make sense to allow them in a float generator, at least on the jvm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call
(gen/fmap float (gen/double* ...))
.
Ah perfect thank you for this 🙏
Although that doesn't work for
##Inf
and##-Inf
. Might not make sense to allow them in a float generator, at least on the jvm.
Yeah that makes sense, I'll try setting infinite?
to always be false
.
(defn parse-float [s] | ||
#?(:clj | ||
(if (string? s) | ||
(try | ||
(Float/parseFloat s) | ||
(catch NumberFormatException _ nil)) | ||
(throw (IllegalArgumentException. | ||
(str "Expected string, got " (if (nil? s) "nil" (-> s class .getName)))))) | ||
:cljs | ||
(parse-double s))) | ||
|
||
(defn -string->float [x] | ||
(if (string? x) | ||
(or (parse-float x) x) | ||
x)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I'm providing a custom definition for parsing floats from strings. I could not find a function that used Float/parseFloat
in this way, so I copied and modified a similar function from Clojure source code.
test/malli/generator_test.cljc
Outdated
|
||
(testing "float properties" | ||
(let [infinity? #(or (= % ##Inf) | ||
(= % ##-Inf)) | ||
NaN? (fn [x] | ||
(#?(:clj (fn [v] | ||
(or (infinity? v) (Float/isNaN v))) | ||
:cljs js/isNaN) | ||
x)) | ||
special? #(or (NaN? %) | ||
(infinity? %)) | ||
test-presence (fn [f options] | ||
(some f (mg/sample [:float options] | ||
{:size 1000})))] | ||
(is (test-presence infinity? {:gen/infinite? true})) | ||
(is (test-presence NaN? {:gen/NaN? true})) | ||
(is (test-presence special? {:gen/infinite? true | ||
:gen/NaN? true})) | ||
(is (not (test-presence special? nil))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was a bit tricky writing this test since I want to use Float/isNaN
, but sometimes when generating Infinity the tests would error because of the value being too big to coerce into a float.
Note, I don't think this test generates Float
instances, so I think it should be updated to check for that but I'm unsure how to do that. If someone know how please let me know 🙏
src/malli/generator.cljc
Outdated
@@ -454,6 +454,20 @@ | |||
{:infinite? (get props :gen/infinite? false) | |||
:NaN? (get props :gen/NaN? false)}) | |||
(-min-max schema options)))) | |||
(defmethod -schema-generator :float [schema options] | |||
(let [max-float #?(:clj Float/MAX_VALUE :cljs (.-MAX_VALUE js/Number)) | |||
min-float #?(:clj Float/MIN_VALUE :cljs (.-MIN_VALUE js/Number)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think Float/MIN_VALUE is the minimum float, I think it's (- Float/MAX_VALUE)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes good catch 🙏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just meant for JVM, (.-MIN_VALUE js/Number)
seems appropriate for cljs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want the min-float
to be a negative number? Because I think (.-MIN_VALUE js/Number)
would be the smallest positive number.
Oh I see, I misread the docs. Do we even need to specify the bounds on cljs since it looks like the same as double?
-------- Original Message --------
…On May 17, 2024, 05:44, Sean Hagstrom wrote:
@seanstrom commented on this pull request.
---------------------------------------------------------------
In [src/malli/generator.cljc](#1055 (comment)):
> @@ -454,6 +454,20 @@
{:infinite? (get props :gen/infinite? false)
:NaN? (get props :gen/NaN? false)})
(-min-max schema options))))
+(defmethod -schema-generator :float [schema options]
+ (let [max-float #?(:clj Float/MAX_VALUE :cljs (.-MAX_VALUE js/Number))
+ min-float #?(:clj Float/MIN_VALUE :cljs (.-MIN_VALUE js/Number))
Do we want the min-float to be a negative number? Because I think (.-MIN_VALUE js/Number) would be [the smallest positive number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_VALUE#description).
—
Reply to this email directly, [view it on GitHub](#1055 (comment)), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/AACGFJF3HTOJIGOJDQ7BWMLZCXNRRAVCNFSM6AAAAABHQPRGQGVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZDANRSHE4DENJXGY).
You are receiving this because you commented.Message ID: ***@***.***>
|
Yeah good point, we probably don't need to do it, so I've changed it to only set the bounds for the clj and allow cljs to behave as doubles. I've also made sure to allow for infinity to be generated for cljs too. |
min-max-props (-min-max schema options) | ||
min-max-clamped #?(:clj {:min (floor-min (:min min-max-props)) | ||
:max (ceil-max (:max min-max-props))} | ||
:cljs min-max-props) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't think of a compelling reason to clamp the bounds, perhaps an error should be thrown instead.
Relatedly, the bounds should be coerced to float so then floating point numbers are generated regardless of bounds like in this PR #1052
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't think of a compelling reason to clamp the bounds, perhaps an error should be thrown instead.
Well the main reason I thought to clamp the bounds is because we're using gen/double*
, which can produce numbers that will lead to thrown exceptions when trying to coerce them into floats. So I think that means we always need to clamp the bounds, but maybe we can also throw an error if someone tries to override those boundaries (?).
the bounds should be coerced to float so then floating point numbers are generated regardless of bounds
I'll push up a commit to coerce the bounds to floats too 👍
Summary
:float
support as described in There's no:float
#722Notes
float
between Clojure and ClojureScript. Specifically, I've tried to make Malli to treat floats as Java/Clojure floats (instances ofFloat
) while usingclj
, and fallback todoubles
while usingcljs