Skip to content

Commit f9c9ce4

Browse files
authored
Deprecate isDigit and digitToInt in favor of functions which specify base (#31)
1 parent a67079a commit f9c9ce4

File tree

4 files changed

+106
-40
lines changed

4 files changed

+106
-40
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ Notable changes to this project are documented in this file. The format is based
55
## [Unreleased]
66

77
Breaking changes (😱!!!):
8+
- Deprecation warnings for `isDigit` and `digitToInt` (#31 by @milesfrain)
89

910
New features:
11+
- Added `hexDigitToInt` `decDigitToInt` `octDigitToInt` (#31 by @milesfrain)
1012

1113
Bugfixes:
1214

packages.dhall

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
let upstream =
2-
https://raw.githubusercontent.com/purescript/package-sets/prepare-0.14/src/packages.dhall sha256:f591635bcfb73053bcb6de2ecbf2896489fa5b580563396f52a1051ede439849
2+
https://raw.githubusercontent.com/purescript/package-sets/prepare-0.14/src/packages.dhall sha256:af6c012eccebfc7447440e486d3ab8fbab02abaa003a540de62e204351d50a98
33

44
in upstream

src/Data/Char/Unicode.purs

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ module Data.Char.Unicode
99
, isAlpha
1010
, isAlphaNum
1111
, isLetter
12-
, isDigit
13-
, isOctDigit
1412
, isHexDigit
13+
, isDecDigit
14+
, isOctDigit
15+
, isDigit -- Deprecated
1516
, isControl
1617
, isPrint
1718
, isSpace
@@ -21,7 +22,11 @@ module Data.Char.Unicode
2122
, isMark
2223
, isNumber
2324

24-
, digitToInt
25+
-- Conversion to Int
26+
, hexDigitToInt
27+
, decDigitToInt
28+
, octDigitToInt
29+
, digitToInt -- Deprecated
2530

2631
-- Case conversion
2732
, toLower
@@ -41,6 +46,7 @@ import Prelude
4146
import Data.Char (toCharCode)
4247
import Data.Char.Unicode.Internal (UnicodeCategory(..), uTowtitle, uTowlower, uTowupper, uIswalnum, uIswalpha, uIswlower, uIswupper, uIswspace, uIswprint, uIswcntrl, uGencat)
4348
import Data.Maybe (Maybe(..))
49+
import Prim.TypeError (class Warn, Text)
4450

4551
-- | Unicode General Categories (column 2 of the UnicodeData table) in
4652
-- | the order they are listed in the Unicode standard (the Unicode
@@ -386,9 +392,9 @@ isAlpha = uIswalpha <<< toCharCode
386392
isAlphaNum :: Char -> Boolean
387393
isAlphaNum = uIswalnum <<< toCharCode
388394

389-
-- | Selects ASCII digits, i.e. `0..9`.
390-
isDigit :: Char -> Boolean
391-
isDigit c = let diff = (toCharCode c - toCharCode '0')
395+
-- | Selects ASCII decimal digits, i.e. `0..9`.
396+
isDecDigit :: Char -> Boolean
397+
isDecDigit c = let diff = (toCharCode c - toCharCode '0')
392398
in diff <= 9 && diff >= 0
393399

394400
-- | Selects ASCII octal digits, i.e. `0..7`.
@@ -399,10 +405,13 @@ isOctDigit c = let diff = (toCharCode c - toCharCode '0')
399405
-- | Selects ASCII hexadecimal digits,
400406
-- | i.e. `0..9, A..F, a..f`.
401407
isHexDigit :: Char -> Boolean
402-
isHexDigit c = isDigit c
408+
isHexDigit c = isDecDigit c
403409
|| (let diff = (toCharCode c - toCharCode 'A') in diff <= 5 && diff >= 0)
404410
|| (let diff = (toCharCode c - toCharCode 'a') in diff <= 5 && diff >= 0)
405411

412+
isDigit :: Warn (Text "'isDigit' is deprecated, use 'isDecDigit', 'isHexDigit', or 'isOctDigit' instead") => Char -> Boolean
413+
isDigit = isDecDigit
414+
406415
-- | Selects Unicode punctuation characters, including various kinds
407416
-- | of connectors, brackets and quotes.
408417
-- |
@@ -523,26 +532,25 @@ toTitle = withCharCode uTowtitle
523532
foreign import withCharCode :: (Int -> Int) -> Char -> Char
524533

525534
-- | Convert a single digit `Char` to the corresponding `Just Int` if its argument
526-
-- | satisfies `isHexDigit`, if it is one of `0..9, A..F, a..f`. Anything else
527-
-- | converts to `Nothing`
528-
-- |
535+
-- | satisfies `isHexDigit` (one of `0..9, A..F, a..f`). Anything else converts to `Nothing`
536+
-- |
529537
-- | ```
530538
-- | >>> import Data.Traversable
531-
-- |
532-
-- | >>> traverse digitToInt ['0','1','2','3','4','5','6','7','8','9']
539+
-- |
540+
-- | >>> traverse hexDigitToInt ['0','1','2','3','4','5','6','7','8','9']
533541
-- | (Just [0,1,2,3,4,5,6,7,8,9])
534-
-- |
535-
-- | >>> traverse digitToInt ['a','b','c','d','e','f']
542+
-- |
543+
-- | >>> traverse hexDigitToInt ['a','b','c','d','e','f']
536544
-- | (Just [10,11,12,13,14,15])
537-
-- |
538-
-- | >>> traverse digitToInt ['A','B','C','D','E','F']
545+
-- |
546+
-- | >>> traverse hexDigitToInt ['A','B','C','D','E','F']
539547
-- | (Just [10,11,12,13,14,15])
540-
-- |
541-
-- | >>> digitToInt 'G'
548+
-- |
549+
-- | >>> hexDigitToInt 'G'
542550
-- | Nothing
543551
-- | ```
544-
digitToInt :: Char -> Maybe Int
545-
digitToInt c = result
552+
hexDigitToInt :: Char -> Maybe Int
553+
hexDigitToInt c = result
546554
where
547555
result :: Maybe Int
548556
result
@@ -560,6 +568,43 @@ digitToInt c = result
560568
hexUpper :: Int
561569
hexUpper = toCharCode c - toCharCode 'A'
562570

571+
-- | Convert a single digit `Char` to the corresponding `Just Int` if its argument
572+
-- | satisfies `isDecDigit` (one of `0..9`). Anything else converts to `Nothing`
573+
-- |
574+
-- | ```
575+
-- | >>> import Data.Traversable
576+
-- |
577+
-- | >>> traverse decDigitToInt ['0','1','2','3','4','5','6','7','8','9']
578+
-- | (Just [0,1,2,3,4,5,6,7,8,9])
579+
-- |
580+
-- | >>> decDigitToInt 'a'
581+
-- | Nothing
582+
-- | ```
583+
decDigitToInt :: Char -> Maybe Int
584+
decDigitToInt c
585+
| isDecDigit c = Just $ toCharCode c - toCharCode '0'
586+
| otherwise = Nothing
587+
588+
-- | Convert a single digit `Char` to the corresponding `Just Int` if its argument
589+
-- | satisfies `isOctDigit` (one of `0..7`). Anything else converts to `Nothing`
590+
-- |
591+
-- | ```
592+
-- | >>> import Data.Traversable
593+
-- |
594+
-- | >>> traverse octDigitToInt ['0','1','2','3','4','5','6','7']
595+
-- | (Just [0,1,2,3,4,5,6,7])
596+
-- |
597+
-- | >>> octDigitToInt '8'
598+
-- | Nothing
599+
-- | ```
600+
octDigitToInt :: Char -> Maybe Int
601+
octDigitToInt c
602+
| isOctDigit c = Just $ toCharCode c - toCharCode '0'
603+
| otherwise = Nothing
604+
605+
digitToInt :: Warn (Text "'digitToInt' is deprecated, use 'decDigitToInt', 'hexDigitToInt', or 'octDigitToInt' instead") => Char -> Maybe Int
606+
digitToInt = hexDigitToInt
607+
563608
-- | Selects alphabetic Unicode characters (lower-case, upper-case and
564609
-- | title-case letters, plus letters of caseless scripts and
565610
-- | modifiers letters). This function is equivalent to

test/Test/Data/Char/Unicode.purs

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import Prelude
55

66
import Control.Monad.Reader.Class (class MonadReader, ask, local)
77
import Data.Char (fromCharCode)
8-
import Data.Char.Unicode (GeneralCategory(..), digitToInt, generalCategory, isAlpha, isAlphaNum, isAscii, isAsciiLower, isAsciiUpper, isControl, isDigit, isHexDigit, isLatin1, isLetter, isLower, isMark, isNumber, isOctDigit, isPrint, isPunctuation, isSeparator, isSpace, isSymbol, isUpper)
8+
import Data.Char.Unicode (GeneralCategory(..), decDigitToInt, hexDigitToInt, octDigitToInt, generalCategory, isAlpha, isAlphaNum, isAscii, isAsciiLower, isAsciiUpper, isControl, isDecDigit, isHexDigit, isLatin1, isLetter, isLower, isMark, isNumber, isOctDigit, isPrint, isPunctuation, isSeparator, isSpace, isSymbol, isUpper)
9+
import Data.Foldable (traverse_)
910
import Data.Maybe (Maybe(..), fromJust)
1011
import Data.Monoid (power, guard)
1112
import Data.NonEmpty ((:|))
13+
import Data.String.CodeUnits (singleton)
1214
import Effect.Console (log)
1315
import Effect.Class (class MonadEffect, liftEffect)
1416
import Partial.Unsafe (unsafePartial)
@@ -53,15 +55,17 @@ dataCharUnicodeTests = describe "module Data.Char.Unicode" do
5355
isUpperTests
5456
isAlphaTests
5557
isAlphaNumTests
56-
isDigitTests
57-
isOctDigitTests
5858
isHexDigitTests
59+
isDecDigitTests
60+
isOctDigitTests
5961
isPunctuationTests
6062
isSymbolTests
6163
toUpperTests
6264
toLowerTests
6365
toTitleTests
64-
digitToIntTests
66+
hexDigitToIntTests
67+
decDigitToIntTests
68+
octDigitToIntTests
6569
isLetterTests
6670
isMarkTests
6771
isNumberTests
@@ -295,10 +299,10 @@ isAlphaNumTests = describe "isAlphaNum" do
295299
it "'\\n' is not AlphaNum" $
296300
isAlphaNum '\n' `shouldEqual` false
297301

298-
isDigitTests :: forall m. MonadReader Int m => MonadEffect m => m Unit
299-
isDigitTests = describe "isDigit" do
300-
it "digits are digits" $ liftEffect $ quickCheck \(AsciiDigit char) -> isDigit char
301-
it "non digits are not digits" $ liftEffect $ quickCheck \(NonAsciiDigit char) -> not $ isDigit char
302+
isDecDigitTests :: forall m. MonadReader Int m => MonadEffect m => m Unit
303+
isDecDigitTests = describe "isDecDigit" do
304+
it "digits are digits" $ liftEffect $ quickCheck \(AsciiDigit char) -> isDecDigit char
305+
it "non digits are not digits" $ liftEffect $ quickCheck \(NonAsciiDigit char) -> not $ isDecDigit char
302306

303307
isOctDigitTests :: forall m. MonadReader Int m => MonadEffect m => m Unit
304308
isOctDigitTests = describe "isOctDigit" do
@@ -352,23 +356,38 @@ toLowerTests = pure unit
352356
toTitleTests :: forall m. MonadReader Int m => MonadEffect m => m Unit
353357
toTitleTests = pure unit
354358

355-
digitToIntTests :: forall m. MonadReader Int m => MonadEffect m => m Unit
356-
digitToIntTests = describe "digitToInt" do
359+
notDigit :: forall m. MonadReader Int m => MonadEffect m =>
360+
String -> (Char -> Maybe Int) -> Char -> m Unit
361+
notDigit base func char =
362+
it ("'" <> singleton char <> "' is not a " <> base <> " digit") $
363+
func char `shouldEqual` Nothing
364+
365+
hexDigitToIntTests :: forall m. MonadReader Int m => MonadEffect m => m Unit
366+
hexDigitToIntTests = describe "hexDigitToInt" do
357367
it "'0'..'9' get mapped correctly" $
358-
map digitToInt ['0','1','2','3','4','5','6','7','8','9'] `shouldEqual`
368+
map hexDigitToInt ['0','1','2','3','4','5','6','7','8','9'] `shouldEqual`
359369
[Just 0, Just 1, Just 2, Just 3, Just 4, Just 5, Just 6, Just 7, Just 8, Just 9]
360370
it "'a'..'f' get mapped correctly" $
361-
map digitToInt ['a','b','c','d','e','f'] `shouldEqual`
371+
map hexDigitToInt ['a','b','c','d','e','f'] `shouldEqual`
362372
[Just 10, Just 11, Just 12, Just 13, Just 14, Just 15]
363373
it "'A'..'F' get mapped correctly" $
364-
map digitToInt ['A','B','C','D','E','F'] `shouldEqual`
374+
map hexDigitToInt ['A','B','C','D','E','F'] `shouldEqual`
365375
[Just 10, Just 11, Just 12, Just 13, Just 14, Just 15]
366-
it "'G' is not a digit" $
367-
digitToInt 'G' `shouldEqual` Nothing
368-
it "'♥' is not a digit" $
369-
digitToInt '♥' `shouldEqual` Nothing
370-
it "'国' is not a digit" $
371-
digitToInt '国' `shouldEqual` Nothing
376+
traverse_ (notDigit "hex" hexDigitToInt) ['g', 'G', '♥', '国']
377+
378+
decDigitToIntTests :: forall m. MonadReader Int m => MonadEffect m => m Unit
379+
decDigitToIntTests = describe "decDigitToInt" do
380+
it "'0'..'9' get mapped correctly" $
381+
map decDigitToInt ['0','1','2','3','4','5','6','7','8','9'] `shouldEqual`
382+
[Just 0, Just 1, Just 2, Just 3, Just 4, Just 5, Just 6, Just 7, Just 8, Just 9]
383+
traverse_ (notDigit "dec" decDigitToInt) ['a', 'A', '♥', '国']
384+
385+
octDigitToIntTests :: forall m. MonadReader Int m => MonadEffect m => m Unit
386+
octDigitToIntTests = describe "octDigitToInt" do
387+
it "'0'..'7' get mapped correctly" $
388+
map octDigitToInt ['0','1','2','3','4','5','6','7'] `shouldEqual`
389+
[Just 0, Just 1, Just 2, Just 3, Just 4, Just 5, Just 6, Just 7]
390+
traverse_ (notDigit "oct" octDigitToInt) ['8', '9', '♥', '国']
372391

373392
isLetterTests:: forall m. MonadReader Int m => MonadEffect m => m Unit
374393
isLetterTests = describe "isLetter" do

0 commit comments

Comments
 (0)