From a0da563b132f619bbb601c3ccbca884141a5813d Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Tue, 17 Apr 2018 01:38:04 +0100 Subject: [PATCH 1/6] Updates for 0.12 --- bower.json | 10 +++++----- test/Main.purs | 7 ++----- test/Test/Data/Enum.purs | 8 ++++---- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/bower.json b/bower.json index 1ea79a6..34694c5 100644 --- a/bower.json +++ b/bower.json @@ -18,12 +18,12 @@ "package.json" ], "dependencies": { - "purescript-either": "^3.0.0", - "purescript-strings": "^3.0.0", - "purescript-unfoldable": "^3.0.0" + "purescript-either": "#compiler/0.12", + "purescript-strings": "#compiler/0.12", + "purescript-unfoldable": "#compiler/0.12" }, "devDependencies": { - "purescript-assert": "^3.0.0", - "purescript-console": "^3.0.0" + "purescript-assert": "#compiler/0.12", + "purescript-console": "#compiler/0.12" } } diff --git a/test/Main.purs b/test/Main.purs index f3eb9ec..b8d6c38 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -2,11 +2,8 @@ module Test.Main where import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE) - -import Test.Assert (ASSERT) +import Effect (Effect) import Test.Data.Enum (testEnum) -main :: Eff (console :: CONSOLE, assert :: ASSERT) Unit +main :: Effect Unit main = testEnum diff --git a/test/Test/Data/Enum.purs b/test/Test/Data/Enum.purs index b6a7c7f..ba49cef 100644 --- a/test/Test/Data/Enum.purs +++ b/test/Test/Data/Enum.purs @@ -2,8 +2,8 @@ module Test.Data.Enum (testEnum) where import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) +import Effect (Effect) +import Effect.Console (log) import Data.Newtype (unwrap) import Data.Enum (class Enum, class BoundedEnum, defaultToEnum, defaultFromEnum, @@ -13,7 +13,7 @@ import Data.Maybe (Maybe(..)) import Data.NonEmpty ((:|)) import Data.Either (Either(..)) -import Test.Assert (ASSERT, assert) +import Test.Assert (assert) data T = A | B | C | D | E @@ -42,7 +42,7 @@ instance boundedEnumT :: BoundedEnum T where toEnum = defaultToEnum fromEnum = defaultFromEnum -testEnum :: Eff (console :: CONSOLE, assert :: ASSERT) Unit +testEnum :: Effect Unit testEnum = do log "enumFromTo" assert $ enumFromTo A A == [A] From 8c10e4342fefc1e19ebafd5d36dc17cb2f989fdc Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 18 May 2018 18:13:52 +0100 Subject: [PATCH 2/6] Don't depend on String for Char enum --- bower.json | 5 +++-- src/Data/Enum.js | 9 +++++++++ src/Data/Enum.purs | 12 +++++++----- 3 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 src/Data/Enum.js diff --git a/bower.json b/bower.json index 34694c5..9cfe057 100644 --- a/bower.json +++ b/bower.json @@ -19,8 +19,9 @@ ], "dependencies": { "purescript-either": "#compiler/0.12", - "purescript-strings": "#compiler/0.12", - "purescript-unfoldable": "#compiler/0.12" + "purescript-unfoldable": "#compiler/0.12", + "purescript-nonempty": "#compiler/0.12", + "purescript-gen": "#compiler/0.12" }, "devDependencies": { "purescript-assert": "#compiler/0.12", diff --git a/src/Data/Enum.js b/src/Data/Enum.js new file mode 100644 index 0000000..126aca0 --- /dev/null +++ b/src/Data/Enum.js @@ -0,0 +1,9 @@ +"use strict"; + +exports.toCharCode = function (c) { + return c.charCodeAt(0); +}; + +exports.fromCharCode = function (c) { + return String.fromCharCode(c); +}; diff --git a/src/Data/Enum.purs b/src/Data/Enum.purs index fb886e6..c5acee2 100644 --- a/src/Data/Enum.purs +++ b/src/Data/Enum.purs @@ -18,7 +18,6 @@ import Prelude import Control.MonadPlus (guard) -import Data.Char (fromCharCode, toCharCode) import Data.Either (Either(..)) import Data.Maybe (Maybe(..), maybe, fromJust) import Data.Newtype (class Newtype, unwrap) @@ -73,10 +72,6 @@ instance enumChar :: Enum Char where succ = defaultSucc charToEnum toCharCode pred = defaultPred charToEnum toCharCode -charToEnum :: Int -> Maybe Char -charToEnum n | n >= bottom && n <= top = Just $ fromCharCode n -charToEnum _ = Nothing - instance enumUnit :: Enum Unit where succ = const Nothing pred = const Nothing @@ -272,3 +267,10 @@ toEnumWithDefaults :: forall a. BoundedEnum a => a -> a -> Int -> a toEnumWithDefaults b t x = case toEnum x of Just enum -> enum Nothing -> if x < fromEnum (bottom :: a) then b else t + +charToEnum :: Int -> Maybe Char +charToEnum n | n >= bottom && n <= top = Just $ fromCharCode n +charToEnum _ = Nothing + +foreign import toCharCode :: Char -> Int +foreign import fromCharCode :: Int -> Char From 9fb28ae500cdf0211eb7823c6d8059540a13c2f8 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sat, 19 May 2018 00:37:06 +0100 Subject: [PATCH 3/6] Remove non-law-abiding instances --- src/Data/Enum.purs | 37 +------------------------------------ test/Test/Data/Enum.purs | 28 +++------------------------- 2 files changed, 4 insertions(+), 61 deletions(-) diff --git a/src/Data/Enum.purs b/src/Data/Enum.purs index c5acee2..284fef8 100644 --- a/src/Data/Enum.purs +++ b/src/Data/Enum.purs @@ -20,7 +20,7 @@ import Control.MonadPlus (guard) import Data.Either (Either(..)) import Data.Maybe (Maybe(..), maybe, fromJust) -import Data.Newtype (class Newtype, unwrap) +import Data.Newtype (class Newtype) import Data.NonEmpty (NonEmpty, (:|)) import Data.Tuple (Tuple(..)) import Data.Unfoldable (class Unfoldable, unfoldr) @@ -202,41 +202,6 @@ instance boundedEnumOrdering :: BoundedEnum Ordering where fromEnum EQ = 1 fromEnum GT = 2 -instance boundedEnumMaybe :: BoundedEnum a => BoundedEnum (Maybe a) where - cardinality = Cardinality $ unwrap (cardinality :: Cardinality a) + 1 - toEnum 0 = pure Nothing - toEnum n = Just <$> toEnum (n - 1) - fromEnum Nothing = 0 - fromEnum (Just e) = fromEnum e + 1 - -instance boundedEnumEither :: (BoundedEnum a, BoundedEnum b) => BoundedEnum (Either a b) where - cardinality = - Cardinality - $ unwrap (cardinality :: Cardinality a) - + unwrap (cardinality :: Cardinality b) - toEnum n = to cardinality - where - to :: Cardinality a -> Maybe (Either a b) - to (Cardinality ca) - | n >= 0 && n < ca = Left <$> toEnum n - | otherwise = Right <$> toEnum (n - ca) - fromEnum (Left a) = fromEnum a - fromEnum (Right b) = fromEnum b + unwrap (cardinality :: Cardinality a) - -instance boundedEnumTuple :: (BoundedEnum a, BoundedEnum b) => BoundedEnum (Tuple a b) where - cardinality = - Cardinality - $ unwrap (cardinality :: Cardinality a) - * unwrap (cardinality :: Cardinality b) - toEnum = to cardinality - where - to :: Cardinality b -> Int -> Maybe (Tuple a b) - to (Cardinality cb) n = Tuple <$> toEnum (n / cb) <*> toEnum (n `mod` cb) - fromEnum = from cardinality - where - from :: Cardinality b -> Tuple a b -> Int - from (Cardinality cb) (Tuple a b) = fromEnum a * cb + fromEnum b - -- | Runs in `O(n)` where `n` is `fromEnum top` defaultCardinality :: forall a. Bounded a => Enum a => Cardinality a defaultCardinality = Cardinality $ defaultCardinality' 1 (bottom :: a) where diff --git a/test/Test/Data/Enum.purs b/test/Test/Data/Enum.purs index ba49cef..8d53d73 100644 --- a/test/Test/Data/Enum.purs +++ b/test/Test/Data/Enum.purs @@ -2,17 +2,11 @@ module Test.Data.Enum (testEnum) where import Prelude -import Effect (Effect) -import Effect.Console (log) - -import Data.Newtype (unwrap) -import Data.Enum (class Enum, class BoundedEnum, defaultToEnum, defaultFromEnum, - defaultCardinality, enumFromTo, enumFromThenTo, upFrom, upFromIncluding, - downFrom, toEnum, fromEnum, Cardinality, cardinality) +import Data.Enum (class BoundedEnum, class Enum, defaultCardinality, defaultFromEnum, defaultToEnum, downFrom, enumFromThenTo, enumFromTo, upFrom, upFromIncluding) import Data.Maybe (Maybe(..)) import Data.NonEmpty ((:|)) -import Data.Either (Either(..)) - +import Effect (Effect) +import Effect.Console (log) import Test.Assert (assert) data T = A | B | C | D | E @@ -71,19 +65,3 @@ testEnum = do assert $ downFrom D == [C, B, A] assert $ downFrom B == [ A] assert $ downFrom A == [ ] - - log "BoundedEnum (Maybe Boolean)" - assert $ toEnum (-1) == Nothing :: Maybe (Maybe Boolean) - assert $ toEnum 0 == Just Nothing :: Maybe (Maybe Boolean) - assert $ toEnum 1 == Just (Just false) :: Maybe (Maybe Boolean) - assert $ toEnum 2 == Just (Just true) :: Maybe (Maybe Boolean) - assert $ toEnum 3 == Nothing :: Maybe (Maybe Boolean) - - log "BoundedEnum (Either _ _)" - assert $ unwrap (cardinality :: Cardinality (Either Boolean Boolean)) == 4 - assert $ toEnum 0 == Just (Left false :: Either Boolean T) - assert $ toEnum 1 == Just (Left true :: Either Boolean T) - assert $ toEnum 3 == Just (Right B :: Either Boolean T) - assert $ fromEnum (Left false :: Either Boolean T) == 0 - assert $ fromEnum (Left true :: Either Boolean T) == 1 - assert $ fromEnum (Right B :: Either Boolean T) == 3 From 5e41a1319a4d5d94437982b585f2d80fdec9d0f4 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sat, 19 May 2018 00:41:55 +0100 Subject: [PATCH 4/6] Update tests to use assertEqual --- test/Test/Data/Enum.purs | 100 +++++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 19 deletions(-) diff --git a/test/Test/Data/Enum.purs b/test/Test/Data/Enum.purs index 8d53d73..5c5d97f 100644 --- a/test/Test/Data/Enum.purs +++ b/test/Test/Data/Enum.purs @@ -7,13 +7,21 @@ import Data.Maybe (Maybe(..)) import Data.NonEmpty ((:|)) import Effect (Effect) import Effect.Console (log) -import Test.Assert (assert) +import Test.Assert (assertEqual) data T = A | B | C | D | E derive instance eqT :: Eq T derive instance ordT :: Ord T +instance showT :: Show T where + show = case _ of + A -> "A" + B -> "B" + C -> "C" + D -> "D" + E -> "E" + instance enumT :: Enum T where succ A = Just B succ B = Just C @@ -39,29 +47,83 @@ instance boundedEnumT :: BoundedEnum T where testEnum :: Effect Unit testEnum = do log "enumFromTo" - assert $ enumFromTo A A == [A] - assert $ enumFromTo B A == [] - assert $ enumFromTo A C == [A, B, C] - assert $ enumFromTo A E == [A, B, C, D, E] + assertEqual + { actual: enumFromTo A A + , expected: [A] + } + assertEqual + { actual: enumFromTo B A + , expected: [] + } + assertEqual + { actual: enumFromTo A C + , expected: [A, B, C] + } + assertEqual + { actual: enumFromTo A E + , expected: [A, B, C, D, E] + } log "enumFromThenTo" - assert $ enumFromThenTo A B E == [A, B, C, D, E] - assert $ enumFromThenTo A C E == [A, C, E] - assert $ enumFromThenTo A E E == [A, E] - assert $ enumFromThenTo A C C == [A, C ] - assert $ enumFromThenTo A C D == [A, C ] + assertEqual + { actual: enumFromThenTo A B E + , expected: [A, B, C, D, E] + } + assertEqual + { actual: enumFromThenTo A C E + , expected: [A, C, E] + } + assertEqual + { actual: enumFromThenTo A E E + , expected: [A, E] + } + assertEqual + { actual: enumFromThenTo A C C + , expected: [A, C] + } + assertEqual + { actual: enumFromThenTo A C D + , expected: [A, C] + } log "upFrom" - assert $ upFrom B == [C, D, E] - assert $ upFrom D == [ E] - assert $ upFrom E == [ ] + assertEqual + { actual: upFrom B + , expected: [C, D, E] + } + assertEqual + { actual: upFrom D + , expected: [E] + } + assertEqual + { actual: upFrom E + , expected: [] + } log "upFromIncluding" - assert $ upFromIncluding B == B :| [C, D, E] - assert $ upFromIncluding D == D :| [ E] - assert $ upFromIncluding E == E :| [ ] + assertEqual + { actual: upFromIncluding B + , expected: B :| [C, D, E] + } + assertEqual + { actual: upFromIncluding D + , expected: D :| [E] + } + assertEqual + { actual: upFromIncluding E + , expected: E :| [] + } log "downFrom" - assert $ downFrom D == [C, B, A] - assert $ downFrom B == [ A] - assert $ downFrom A == [ ] + assertEqual + { actual: downFrom D + , expected: [C, B, A] + } + assertEqual + { actual: downFrom B + , expected: [A] + } + assertEqual + { actual: downFrom A + , expected: [] + } From acd7f32faff2227e8e3541644288ee82095a97f7 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sun, 20 May 2018 15:24:11 +0100 Subject: [PATCH 5/6] More updates, tests, doc comments --- src/Data/Enum.purs | 259 ++++++++++++++++++++++++--------------- test/Test/Data/Enum.purs | 30 ++++- 2 files changed, 189 insertions(+), 100 deletions(-) diff --git a/src/Data/Enum.purs b/src/Data/Enum.purs index 284fef8..7375e47 100644 --- a/src/Data/Enum.purs +++ b/src/Data/Enum.purs @@ -1,14 +1,16 @@ module Data.Enum ( class Enum, succ, pred - , defaultSucc - , defaultPred + , class BoundedEnum, cardinality, toEnum, fromEnum + , toEnumWithDefaults + , Cardinality(..) , enumFromTo , enumFromThenTo , upFrom , upFromIncluding , downFrom - , Cardinality(..) - , class BoundedEnum, cardinality, toEnum, fromEnum, toEnumWithDefaults + , downFromIncluding + , defaultSucc + , defaultPred , defaultCardinality , defaultToEnum , defaultFromEnum @@ -17,24 +19,14 @@ module Data.Enum import Prelude import Control.MonadPlus (guard) - import Data.Either (Either(..)) import Data.Maybe (Maybe(..), maybe, fromJust) import Data.Newtype (class Newtype) -import Data.NonEmpty (NonEmpty, (:|)) import Data.Tuple (Tuple(..)) -import Data.Unfoldable (class Unfoldable, unfoldr) - +import Data.Unfoldable (class Unfoldable, singleton, unfoldr) +import Data.Unfoldable1 (class Unfoldable1, unfoldr1) import Partial.Unsafe (unsafePartial) -newtype Cardinality a = Cardinality Int - -derive instance newtypeCardinality :: Newtype (Cardinality a) _ - -derive newtype instance eqCardinality :: Eq (Cardinality a) - -derive newtype instance ordCardinality :: Ord (Cardinality a) - -- | Type class for enumerations. -- | -- | Laws: @@ -49,11 +41,12 @@ derive newtype instance ordCardinality :: Ord (Cardinality a) -- | the opposite of `pred`; if you apply `succ` and then `pred` to something, -- | you should end up with what you started with (although of course this -- | doesn't apply if you tried to `succ` the last value in an enumeration and --- | therefore got `Nothing` out). The non-skipping laws can intuitively be --- | understood as saying that `succ` shouldn't skip over any elements of your --- | type. For example, without the non-skipping laws, it would be permissible --- | to write an `Enum Int` instance where `succ x = Just (x+2)`, and similarly --- | `pred x = Just (x-2)`. +-- | therefore got `Nothing` out). +-- | +-- | The non-skipping laws can intuitively be understood as saying that `succ` +-- | shouldn't skip over any elements of your type. For example, _without_ the +-- | non-skipping laws, it would be permissible to write an `Enum Int` instance +-- | where `succ x = Just (x+2)`, and similarly `pred x = Just (x-2)`. class Ord a <= Enum a where succ :: a -> Maybe a pred :: a -> Maybe a @@ -85,78 +78,27 @@ instance enumOrdering :: Enum Ordering where pred GT = Just EQ instance enumMaybe :: BoundedEnum a => Enum (Maybe a) where - succ Nothing = Just $ Just bottom + succ Nothing = Just (Just bottom) succ (Just a) = Just <$> succ a pred Nothing = Nothing - pred (Just a) = Just $ pred a + pred (Just a) = Just (pred a) instance enumEither :: (BoundedEnum a, BoundedEnum b) => Enum (Either a b) where - succ (Left a) = maybe (Just $ Right bottom) (Just <<< Left) (succ a) - succ (Right b) = maybe (Nothing) (Just <<< Right) (succ b) - pred (Left a) = maybe (Nothing) (Just <<< Left) (pred a) - pred (Right b) = maybe (Just $ Left top) (Just <<< Right) (pred b) + succ (Left a) = maybe (Just (Right bottom)) (Just <<< Left) (succ a) + succ (Right b) = maybe Nothing (Just <<< Right) (succ b) + pred (Left a) = maybe Nothing (Just <<< Left) (pred a) + pred (Right b) = maybe (Just (Left top)) (Just <<< Right) (pred b) instance enumTuple :: (Enum a, BoundedEnum b) => Enum (Tuple a b) where succ (Tuple a b) = maybe (flip Tuple bottom <$> succ a) (Just <<< Tuple a) (succ b) pred (Tuple a b) = maybe (flip Tuple top <$> pred a) (Just <<< Tuple a) (pred b) --- | ```defaultSucc toEnum fromEnum = succ``` -defaultSucc :: forall a. (Int -> Maybe a) -> (a -> Int) -> a -> Maybe a -defaultSucc toEnum' fromEnum' a = toEnum' (fromEnum' a + 1) - --- | ```defaultPred toEnum fromEnum = pred``` -defaultPred :: forall a. (Int -> Maybe a) -> (a -> Int) -> a -> Maybe a -defaultPred toEnum' fromEnum' a = toEnum' (fromEnum' a - 1) - --- | Returns a successive sequence of elements from the lower bound to --- | the upper bound (inclusive). -enumFromTo :: forall a u. Enum a => Unfoldable u => a -> a -> u a -enumFromTo from to = unfoldr go (Just from) - where - go mx = do - x <- mx - guard (x <= to) - pure $ Tuple x (succ x) - --- | `[a,b..c]` -enumFromThenTo :: forall a. BoundedEnum a => a -> a -> a -> Array a -enumFromThenTo = unsafePartial \a b c -> - let a' = fromEnum a - b' = fromEnum b - c' = fromEnum c - in (toEnum >>> fromJust) <$> intStepFromTo (b' - a') a' c' - --- | Property: ```forall e in intStepFromTo step a b: a <= e <= b``` -intStepFromTo :: Int -> Int -> Int -> Array Int -intStepFromTo step from to = - unfoldr (\e -> - if e <= to - then Just $ Tuple e (e + step) -- Output the value e, set the next state to (e + step) - else Nothing -- End of the collection. - ) from - -diag :: forall a. a -> Tuple a a -diag a = Tuple a a - --- | Results in all successors from given Enum in some unfoldable. --- | Note that given Enum is not included in the result. -upFrom :: forall a u. Enum a => Unfoldable u => a -> u a -upFrom = unfoldr (map diag <<< succ) - --- | Results in all successors of given Enum (including itself) --- | in some NonEmpty unfoldable. -upFromIncluding :: ∀ a u. Enum a => Unfoldable u => a -> NonEmpty u a -upFromIncluding x = x :| upFrom x - -downFrom :: forall a u. Enum a => Unfoldable u => a -> u a -downFrom = unfoldr (map diag <<< pred) - -- | Type class for finite enumerations. -- | -- | This should not be considered a part of a numeric hierarchy, as in Haskell. -- | Rather, this is a type class for small, ordered sum types with -- | statically-determined cardinality and the ability to easily compute --- | successor and predecessor elements, e.g. `DayOfWeek`. +-- | successor and predecessor elements like `DayOfWeek`. -- | -- | Laws: -- | @@ -202,22 +144,6 @@ instance boundedEnumOrdering :: BoundedEnum Ordering where fromEnum EQ = 1 fromEnum GT = 2 --- | Runs in `O(n)` where `n` is `fromEnum top` -defaultCardinality :: forall a. Bounded a => Enum a => Cardinality a -defaultCardinality = Cardinality $ defaultCardinality' 1 (bottom :: a) where - defaultCardinality' i = maybe i (defaultCardinality' $ i + 1) <<< succ - - -- | Runs in `O(n)` where `n` is `fromEnum a` -defaultToEnum :: forall a. Bounded a => Enum a => Int -> Maybe a -defaultToEnum n - | n < 0 = Nothing - | n == 0 = Just bottom - | otherwise = defaultToEnum (n - 1) >>= succ - - -- | Runs in `O(n)` where `n` is `fromEnum a` -defaultFromEnum :: forall a. Enum a => a -> Int -defaultFromEnum = maybe 0 (\prd -> defaultFromEnum prd + 1) <<< pred - -- | Like `toEnum` but returns the first argument if `x` is less than -- | `fromEnum bottom` and the second argument if `x` is greater than -- | `fromEnum top`. @@ -229,12 +155,149 @@ defaultFromEnum = maybe 0 (\prd -> defaultFromEnum prd + 1) <<< pred -- | toEnumWithDefaults False True 2 -- True -- | ``` toEnumWithDefaults :: forall a. BoundedEnum a => a -> a -> Int -> a -toEnumWithDefaults b t x = case toEnum x of +toEnumWithDefaults low high x = case toEnum x of Just enum -> enum - Nothing -> if x < fromEnum (bottom :: a) then b else t + Nothing -> if x < fromEnum (bottom :: a) then low else high + +-- | A type for the size of finite enumerations. +newtype Cardinality a = Cardinality Int + +derive instance newtypeCardinality :: Newtype (Cardinality a) _ +derive newtype instance eqCardinality :: Eq (Cardinality a) +derive newtype instance ordCardinality :: Ord (Cardinality a) + +instance showCardinality :: Show (Cardinality a) where + show (Cardinality n) = "(Cardinality " <> show n <> ")" + +-- | Returns a contiguous sequence of elements from the first value to the +-- | second value (inclusive). +-- | +-- | ``` purescript +-- | enumFromTo 0 3 = [0, 1, 2, 3] +-- | enumFromTo 'c' 'a' = ['c', 'b', 'a'] +-- | ``` +-- | +-- | The example shows `Array` return values, but the result can be any type +-- | with an `Unfoldable1` instance. +enumFromTo :: forall a u. Enum a => Unfoldable1 u => a -> a -> u a +enumFromTo = case _, _ of + from, to + | from == to -> singleton from + | from < to -> unfoldr1 (go succ (<=) to) from + | otherwise -> unfoldr1 (go pred (>=) to) from + where + go step op to a = Tuple a (step a >>= \a' -> guard (a' `op` to) $> a') + +-- | Returns a sequence of elements from the first value, taking steps +-- | according to the difference between the first and second value, up to +-- | (but not exceeding) the third value. +-- | +-- | ``` purescript +-- | enumFromThenTo 0 2 6 = [0, 2, 4, 6] +-- | enumFromThenTo 0 3 5 = [0, 3] +-- | ``` +-- | +-- | Note that there is no `BoundedEnum` instance for integers, they're just +-- | being used here for illustrative purposes to help clarify the behaviour. +-- | +-- | The example shows `Array` return values, but the result can be any type +-- | with an `Unfoldable1` instance. +enumFromThenTo :: forall f a. Unfoldable f => Functor f => BoundedEnum a => a -> a -> a -> f a +enumFromThenTo = unsafePartial \a b c -> + let + a' = fromEnum a + b' = fromEnum b + c' = fromEnum c + in + (toEnum >>> fromJust) <$> unfoldr (go (b' - a') c') a' + where + go step to e + | e <= to = Just (Tuple e (e + step)) + | otherwise = Nothing + +-- | Produces all successors of an `Enum` value, excluding the start value. +upFrom :: forall a u. Enum a => Unfoldable u => a -> u a +upFrom = unfoldr (map diag <<< succ) + +-- | Produces all successors of an `Enum` value, including the start value. +-- | +-- | `upFromIncluding bottom` will return all values in an `Enum`. +upFromIncluding :: ∀ a u. Enum a => Unfoldable1 u => a -> u a +upFromIncluding = unfoldr1 (Tuple <*> succ) + +-- | Produces all predecessors of an `Enum` value, excluding the start value. +downFrom :: forall a u. Enum a => Unfoldable u => a -> u a +downFrom = unfoldr (map diag <<< pred) + +-- | Produces all predecessors of an `Enum` value, including the start value. +-- | +-- | `downFromIncluding top` will return all values in an `Enum`, in reverse +-- | order. +downFromIncluding :: forall a u. Enum a => Unfoldable1 u => a -> u a +downFromIncluding = unfoldr1 (Tuple <*> pred) + +-- | Provides a default implementation for `succ`, given a function that maps +-- | integers to values in the `Enum`, and a function that maps values in the +-- | `Enum` back to integers. The integer mapping must agree in both directions +-- | for this to implement a law-abiding `succ`. +-- | +-- | If a `BoundedEnum` instance exists for `a`, the `toEnum` and `fromEnum` +-- | functions can be used here: +-- | +-- | ``` purescript +-- | succ = defaultSucc toEnum fromEnum +-- | ``` +defaultSucc :: forall a. (Int -> Maybe a) -> (a -> Int) -> a -> Maybe a +defaultSucc toEnum' fromEnum' a = toEnum' (fromEnum' a + 1) + +-- | Provides a default implementation for `pred`, given a function that maps +-- | integers to values in the `Enum`, and a function that maps values in the +-- | `Enum` back to integers. The integer mapping must agree in both directions +-- | for this to implement a law-abiding `pred`. +-- | +-- | If a `BoundedEnum` instance exists for `a`, the `toEnum` and `fromEnum` +-- | functions can be used here: +-- | +-- | ``` purescript +-- | pred = defaultPred toEnum fromEnum +-- | ``` +defaultPred :: forall a. (Int -> Maybe a) -> (a -> Int) -> a -> Maybe a +defaultPred toEnum' fromEnum' a = toEnum' (fromEnum' a - 1) + +-- | Provides a default implementation for `cardinality`. +-- | +-- | Runs in `O(n)` where `n` is `fromEnum top` +defaultCardinality :: forall a. Bounded a => Enum a => Cardinality a +defaultCardinality = Cardinality $ defaultCardinality' 1 (bottom :: a) + where + defaultCardinality' i = maybe i (defaultCardinality' (i + 1)) <<< succ + +-- | Provides a default implementation for `toEnum`. +-- | +-- | - Assumes `fromEnum bottom = 0`. +-- | - Cannot be used in conjuction with `defaultSucc`. +-- | +-- | Runs in `O(n)` where `n` is `fromEnum a`. +defaultToEnum :: forall a. Bounded a => Enum a => Int -> Maybe a +defaultToEnum n + | n < 0 = Nothing + | n == 0 = Just bottom + | otherwise = defaultToEnum (n - 1) >>= succ + +-- | Provides a default implementation for `fromEnum`. +-- | +-- | - Assumes `toEnum 0 = Just bottom`. +-- | - Cannot be used in conjuction with `defaultPred`. +-- | +-- | Runs in `O(n)` where `n` is `fromEnum a`. +defaultFromEnum :: forall a. Enum a => a -> Int +defaultFromEnum = maybe 0 (\prd -> defaultFromEnum prd + 1) <<< pred + +diag :: forall a. a -> Tuple a a +diag a = Tuple a a charToEnum :: Int -> Maybe Char -charToEnum n | n >= bottom && n <= top = Just $ fromCharCode n +charToEnum n | n >= bottom && n <= top = Just (fromCharCode n) charToEnum _ = Nothing foreign import toCharCode :: Char -> Int diff --git a/test/Test/Data/Enum.purs b/test/Test/Data/Enum.purs index 5c5d97f..0b77e3c 100644 --- a/test/Test/Data/Enum.purs +++ b/test/Test/Data/Enum.purs @@ -2,7 +2,7 @@ module Test.Data.Enum (testEnum) where import Prelude -import Data.Enum (class BoundedEnum, class Enum, defaultCardinality, defaultFromEnum, defaultToEnum, downFrom, enumFromThenTo, enumFromTo, upFrom, upFromIncluding) +import Data.Enum (class BoundedEnum, class Enum, defaultCardinality, defaultFromEnum, defaultToEnum, downFrom, downFromIncluding, enumFromThenTo, enumFromTo, upFrom, upFromIncluding) import Data.Maybe (Maybe(..)) import Data.NonEmpty ((:|)) import Effect (Effect) @@ -53,7 +53,7 @@ testEnum = do } assertEqual { actual: enumFromTo B A - , expected: [] + , expected: [B, A] } assertEqual { actual: enumFromTo A C @@ -63,6 +63,14 @@ testEnum = do { actual: enumFromTo A E , expected: [A, B, C, D, E] } + assertEqual + { actual: enumFromTo 0 3 + , expected: [0, 1, 2, 3] + } + assertEqual + { actual: enumFromTo 'c' 'a' + , expected: ['c', 'b', 'a'] + } log "enumFromThenTo" assertEqual @@ -101,6 +109,10 @@ testEnum = do } log "upFromIncluding" + assertEqual + { actual: upFromIncluding B + , expected: [B, C, D, E] + } assertEqual { actual: upFromIncluding B , expected: B :| [C, D, E] @@ -127,3 +139,17 @@ testEnum = do { actual: downFrom A , expected: [] } + + log "downFromIncluding" + assertEqual + { actual: downFromIncluding D + , expected: [D, C, B, A] + } + assertEqual + { actual: downFromIncluding B + , expected: [B, A] + } + assertEqual + { actual: downFromIncluding A + , expected: [A] + } From ad8db13887875180996a13e9a711d14996e74c95 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Wed, 23 May 2018 20:29:29 +0100 Subject: [PATCH 6/6] Update dependencies, license --- .gitignore | 1 + LICENSE | 38 ++++++++++++++++++++++---------------- bower.json | 21 +++++++++++++-------- package.json | 6 +++--- 4 files changed, 39 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 307f9c0..b215c44 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /bower_components/ /node_modules/ /output/ +package-lock.json diff --git a/LICENSE b/LICENSE index 58b0299..311379c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,20 +1,26 @@ -The MIT License (MIT) +Copyright 2018 PureScript -Copyright (c) 2014 PureScript +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bower.json b/bower.json index 9cfe057..bf03fdb 100644 --- a/bower.json +++ b/bower.json @@ -1,8 +1,7 @@ { "name": "purescript-enums", "homepage": "https://github.com/purescript/purescript-enums", - "description": "Operations for sequentially ordered types", - "license": "MIT", + "license": "BSD-3-Clause", "repository": { "type": "git", "url": "git://github.com/purescript/purescript-enums.git" @@ -18,13 +17,19 @@ "package.json" ], "dependencies": { - "purescript-either": "#compiler/0.12", - "purescript-unfoldable": "#compiler/0.12", - "purescript-nonempty": "#compiler/0.12", - "purescript-gen": "#compiler/0.12" + "purescript-control": "^4.0.0", + "purescript-either": "^4.0.0", + "purescript-gen": "^2.0.0", + "purescript-maybe": "^4.0.0", + "purescript-newtype": "^3.0.0", + "purescript-nonempty": "^5.0.0", + "purescript-partial": "^2.0.0", + "purescript-prelude": "^4.0.0", + "purescript-tuples": "^5.0.0", + "purescript-unfoldable": "^4.0.0" }, "devDependencies": { - "purescript-assert": "#compiler/0.12", - "purescript-console": "#compiler/0.12" + "purescript-assert": "^4.0.0", + "purescript-console": "^4.0.0" } } diff --git a/package.json b/package.json index f1deb35..5a4bff0 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "test": "pulp test" }, "devDependencies": { - "pulp": "^11.0.0", - "purescript-psa": "^0.5.1", - "rimraf": "^2.6.1" + "pulp": "^12.2.0", + "purescript-psa": "^0.6.0", + "rimraf": "^2.6.2" } }