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

Port Number-related code from deprecated purescript-globals into this repo #12

Merged
merged 21 commits into from
Nov 12, 2020
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
800ea85
Update URL in bower.json file to purescript GH organization
JordanMartinez Oct 23, 2020
49f6da4
Remove `globals` as a dependency
JordanMartinez Oct 23, 2020
40b755c
Copy `Global.Unsafe` module from `globals` into this repo
JordanMartinez Oct 24, 2020
db8e0a8
Copy over Global.purs from `globals` but exclude URI-related code
JordanMartinez Oct 24, 2020
46366de
Port `fromString` from Number.purs into Gobal.purs
JordanMartinez Oct 24, 2020
0641b14
Replace original Data.Number.purs file with Global.purs file
JordanMartinez Oct 24, 2020
5ef669b
Copy test code from `globals` into this repo, minux URI-related tests
JordanMartinez Oct 24, 2020
078d554
Update globals' test file's imports
JordanMartinez Oct 24, 2020
f97cb7d
Distinguish globals test code from (incoming) original numbers test code
JordanMartinez Oct 24, 2020
6e15ba4
Move numbers original test file content into globals test file
JordanMartinez Oct 24, 2020
10b7f12
Rename numbers test code's `main` to `numbersTestCode`
JordanMartinez Oct 24, 2020
92f7de4
Merge imports in test code
JordanMartinez Oct 24, 2020
0eed5c0
Remove original repo's test/Main.purs file
JordanMartinez Oct 24, 2020
f5d5808
Use `readFloat` directly
JordanMartinez Oct 24, 2020
95b031f
Remove duplicate `fromString` export
JordanMartinez Oct 24, 2020
f78d26c
Remove 'readInt' from repo
JordanMartinez Nov 12, 2020
84a71e4
Remove `readFloat` while preserving `fromString` implementation
JordanMartinez Nov 12, 2020
30bea47
Remove toFixed, toPrecision, and toExponential from repo
JordanMartinez Nov 12, 2020
1ce7c59
Fix compiler warning: remove redundant import
JordanMartinez Nov 12, 2020
1d5f112
Remove unsafeToFixed, unsafeToPrecision, and unsafeToExponential from…
JordanMartinez Nov 12, 2020
6d7695a
Cleanup imports by putting them on one line
JordanMartinez Nov 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
"name": "purescript-numbers",
"description": "Functions for working with Numbers",
"license": "MIT",
"homepage": "https://github.com/sharkdp/purescript-numbers",
"homepage": "https://github.com/purescript/purescript-numbers",
"authors": [
"David Peter <mail@david-peter.de>"
],
"repository": {
"type": "git",
"url": "git://github.com/sharkdp/purescript-numbers.git"
"url": "git://github.com/purescript/purescript-numbers.git"
},
"ignore": [
"**/.*",
Expand All @@ -17,8 +17,8 @@
],
"dependencies": {
"purescript-math": "master",
"purescript-globals": "master",
"purescript-maybe": "master"
"purescript-maybe": "master",
"purescript-functions": "master"
},
"devDependencies": {
"purescript-console": "master",
Expand Down
33 changes: 33 additions & 0 deletions src/Data/Number.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* globals exports */
"use strict";

exports.nan = NaN;

exports.isNaN = isNaN;

exports.infinity = Infinity;

exports.isFinite = isFinite;

exports.readInt = function (radix) {
return function (n) {
return parseInt(n, radix);
};
};

exports.readFloat = parseFloat;

var formatNumber = function (format) {
return function (fail, succ, digits, n) {
try {
return succ(n[format](digits));
}
catch (e) {
return fail(e.message);
}
};
};

exports._toFixed = formatNumber("toFixed");
exports._toExponential = formatNumber("toExponential");
exports._toPrecision = formatNumber("toPrecision");
62 changes: 47 additions & 15 deletions src/Data/Number.purs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,34 @@ module Data.Number
, isNaN
, infinity
, isFinite
, readInt
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think it makes to export readInt from this module, as readInt doesn't really have much to do with numbers. Additionally, Data.Int already provides a fromString which does what we want.

Copy link
Contributor Author

@JordanMartinez JordanMartinez Nov 12, 2020

Choose a reason for hiding this comment

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

While it does provide fromString, readInt can parse a String into a specified base. Should we port this function (albeit under a different name) to purescript-integers? Or should it be dropped altogether?

Copy link
Collaborator

Choose a reason for hiding this comment

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

That's a good point. I think we should just drop it for now; I think we can add integer parsing in a specified base to purescript-integers if and when someone asks for it.

, readFloat
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think it makes sense to export this function, as it's just a less safe version of fromString.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed.

, toFixed
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would rather not export toFixed, toExponential, or toPrecision here, since formatting numbers is already covered by Data.Number.Format.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed.

, toExponential
, toPrecision
) where

import Prelude

import Data.Function.Uncurried (Fn4, runFn4)
import Data.Maybe (Maybe(..))
import Global as G

-- | Not a number (NaN)
foreign import nan :: Number

-- | Test whether a number is NaN
foreign import isNaN :: Number -> Boolean

-- | Positive infinity
foreign import infinity :: Number

-- | Test whether a number is finite
foreign import isFinite :: Number -> Boolean

-- | Parse an integer from a `String` in the specified base
foreign import readInt :: Int -> String -> Number

-- | Parse a floating point value from a `String`
foreign import readFloat :: String -> Number

-- | Attempt to parse a `Number` using JavaScripts `parseFloat`. Returns
-- | `Nothing` if the parse fails or if the result is not a finite number.
Expand Down Expand Up @@ -40,23 +62,33 @@ import Global as G
-- | (Just 1.2)
-- | ```
fromString :: String -> Maybe Number
fromString = G.readFloat >>> check
fromString = readFloat >>> check
where
check num | isFinite num = Just num
| otherwise = Nothing

-- | Not a number (NaN).
nan :: Number
nan = G.nan
foreign import _toFixed :: forall a. Fn4 (String -> a) (String -> a) Int Number a

foreign import _toExponential :: forall a. Fn4 (String -> a) (String -> a) Int Number a

foreign import _toPrecision :: forall a. Fn4 (String -> a) (String -> a) Int Number a

-- | Test whether a `Number` is NaN.
isNaN :: Number -> Boolean
isNaN = G.isNaN
-- | Formats Number as a String with limited number of digits after the dot.
-- | May return `Nothing` when specified number of digits is less than 0 or
-- | greater than 20. See ECMA-262 for more information.
toFixed :: Int -> Number -> Maybe String
toFixed digits n = runFn4 _toFixed (const Nothing) Just digits n

-- | Positive infinity.
infinity :: Number
infinity = G.infinity
-- | Formats Number as String in exponential notation limiting number of digits
-- | after the decimal dot. May return `Nothing` when specified number of
-- | digits is less than 0 or greater than 20 depending on the implementation.
-- | See ECMA-262 for more information.
toExponential :: Int -> Number -> Maybe String
toExponential digits n = runFn4 _toExponential (const Nothing) Just digits n

-- | Test whether a number is finite.
isFinite :: Number -> Boolean
isFinite = G.isFinite
-- | Formats Number as String in fixed-point or exponential notation rounded
-- | to specified number of significant digits. May return `Nothing` when
-- | precision is less than 1 or greater than 21 depending on the
-- | implementation. See ECMA-262 for more information.
toPrecision :: Int -> Number -> Maybe String
toPrecision digits n = runFn4 _toPrecision (const Nothing) Just digits n
20 changes: 20 additions & 0 deletions src/Data/Number/Unsafe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* globals exports, JSON */
"use strict";

exports.unsafeToFixed = function (digits) {
return function (n) {
return n.toFixed(digits);
};
};

exports.unsafeToExponential = function (digits) {
return function (n) {
return n.toExponential(digits);
};
};

exports.unsafeToPrecision = function (digits) {
return function (n) {
return n.toPrecision(digits);
};
};
21 changes: 21 additions & 0 deletions src/Data/Number/Unsafe.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Data.Number.Unsafe where

-- | Formats Number as a String with limited number of digits after the dot.
-- |
-- | May throw RangeError if the number of digits is not within the allowed range
-- | (standard precision range is 0 to 20, but implementations may change it)
foreign import unsafeToFixed :: Int -> Number -> String
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd prefer not to export these either, since they're more JS-specific in how they handle errors, whereas the core libraries ought to be backend-independent as far as is possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. Should this be moved to a separate repo outside of core? Or do you think it's fine to drop them entirely?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it's fine to drop them entirely: it's hard for me to imagine a situation where you'd want these functions to be able to throw.


-- | Formats Number as String in exponential notation limiting number of digits
-- | after the decimal dot.
-- |
-- | May throw RangeError if the number of digits is not within the allowed range
-- | (standard precision range is 0 to 20, but implementations may change it)
foreign import unsafeToExponential :: Int -> Number -> String

-- | Formats Number as String in fixed-point or exponential notation rounded
-- | to specified number of significant digits.
-- |
-- | May throw RangeError if the number of digits is not within the allowed range
-- | (standard precision range is 0 to 100, but implementations may change it)
foreign import unsafeToPrecision :: Int -> Number -> String
93 changes: 87 additions & 6 deletions test/Main.purs → test/Test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,97 @@ module Test.Main where
import Prelude

import Data.Maybe (Maybe(..), fromMaybe)
import Data.Number (nan, isNaN, infinity, isFinite, fromString)
import Effect (Effect)
import Effect.Console (log)

import Data.Number (readFloat, readInt, isFinite, infinity,
nan, isNaN, fromString,
toPrecision, toExponential, toFixed)
import Data.Number.Format (precision, fixed, exponential, toStringWith,
toString)
import Data.Number.Approximate (Fraction(..), Tolerance(..), eqRelative,
eqAbsolute, (≅), (≇))
import Data.Number.Unsafe (unsafeToPrecision, unsafeToExponential, unsafeToFixed)

import Effect (Effect)
import Effect.Console (log)
import Test.Assert (assertTrue', assertFalse', assertEqual)
import Test.Assert (assert, assertTrue', assertFalse', assertEqual)

main :: Effect Unit
main = do
globalsTestCode
numbersTestCode

-- Test code for the `purescript-globals` repo before its' Number-related
-- code was moved into this repo
globalsTestCode :: Effect Unit
globalsTestCode = do
let num = 12345.6789

log "nan /= nan"
assert $ nan /= nan

log "not (isNaN 6.0)"
assert $ not (isNaN 6.0)

log "isNaN nan"
assert $ isNaN nan

log "infinity > 0.0"
assert $ infinity > 0.0

log "-infinity < 0.0"
assert $ -infinity < 0.0

log "not (isFinite infinity)"
assert $ not (isFinite infinity)

log "isFinite 0.0"
assert $ isFinite 0.0

log "readInt 16 \"0xFF\" == 255.0"
assert $ readInt 16 "0xFF" == 255.0

log "readInt 10 \"3.5\" == 3.0"
assert $ readInt 10 "3.5" == 3.0

log "readFloat \"3.5\" == 3.5"
assert $ readFloat "3.5" == 3.5

-- note the rounding
log $ "unsafeToFixed 1" <> (show num) <> " == \"12345.7\""
assert $ unsafeToFixed 1 num == "12345.7"

-- padded with zeros
log $ "unsafeToFixed 6" <> (show num) <> " == \"12345.678900\""
assert $ unsafeToFixed 6 num == "12345.678900"

log $ "unsafeToExponential 4" <> (show num) <> " == \"1.2346e+4\""
assert $ unsafeToExponential 4 num == "1.2346e+4"

log $ "unsafeToPrecision 3" <> (show num) <> " == \"1.23e+4\""
assert $ unsafeToPrecision 3 num == "1.23e+4"

log $ "unsafeToPrecision 6" <> (show num) <> " == \"12345.7\""
assert $ unsafeToPrecision 6 num == "12345.7"

-- note the rounding
log $ "toFixed 1" <> (show num) <> " == (Just \"12345.7\")"
assert $ toFixed 1 num == Just "12345.7"

-- padded with zeros
log $ "toFixed 6" <> (show num) <> " == (Just \"12345.678900\")"
assert $ toFixed 6 num == Just "12345.678900"

log $ "toExponential 4" <> (show num) <> " == (Just \"1.2346e+4\")"
assert $ toExponential 4 num == Just "1.2346e+4"

log $ "toPrecision 3" <> (show num) <> " == (Just \"1.23e+4\")"
assert $ toPrecision 3 num == Just "1.23e+4"

log $ "toPrecision 6" <> (show num) <> " == (Just \"12345.7\")"
assert $ toPrecision 6 num == Just "12345.7"

-- Test code originally in this repo before parts of deprecated
-- `purescript-globals` repo was moved to this repo.

-- | Comparison up to 10% relative error.
eqRelative' :: Number -> Number -> Boolean
Expand All @@ -26,8 +107,8 @@ eqAbsolute' = eqAbsolute (Tolerance 0.1)

infix 1 eqAbsolute' as =~=

main :: Effect Unit
main = do
numbersTestCode :: Effect Unit
numbersTestCode = do


log "Data.Number.fromString"
Expand Down