@@ -9,13 +9,22 @@ module Data.Enum
99 , runCardinality
1010 , succ
1111 , toEnum
12+ , defaultSucc
13+ , defaultPred
14+ , defaultToEnum
15+ , defaultFromEnum
16+ , intFromTo
17+ , intStepFromTo
18+ , enumFromTo
19+ , enumFromThenTo
1220 ) where
1321
1422 import Data.Maybe
1523 import Data.Either
1624 import Data.Tuple
1725 import Data.Char
1826 import Data.Maybe.Unsafe
27+ import Data.Unfoldable
1928
2029 newtype Cardinality a = Cardinality Number
2130
@@ -28,13 +37,23 @@ module Data.Enum
2837 -- | to easily compute successor and predecessor elements. e.g. DayOfWeek, etc.
2938 -- |
3039 -- | Laws:
31- -- | succ firstEnum >>= succ >>= succ ... succ [cardinality times] == lastEnum
32- -- | pred lastEnum >>= pred >>= pred ... pred [cardinality times] == firstEnum
33- -- |
34- -- | Just $ e1 `compare` e2 == fromEnum e1 `compare` fromEnum e2
35- -- |
40+ -- | succ firstEnum >>= succ >>= succ ... succ [cardinality - 1 times] == lastEnum
41+ -- | pred lastEnum >>= pred >>= pred ... pred [cardinality - 1 times] == firstEnum
42+ -- |
43+ -- | e1 `compare` e2 == fromEnum e1 `compare` fromEnum e2
44+ -- |
3645 -- | for all a > firstEnum: pred a >>= succ == Just a
3746 -- | for all a < lastEnum: succ a >>= pred == Just a
47+ -- |
48+ -- | pred >=> succ >=> pred = pred
49+ -- | succ >=> pred >=> succ = succ
50+ -- |
51+ -- | toEnum (fromEnum a) = Just a
52+ -- |
53+ -- | for all a > firstEnum: fromEnum <$> pred a = Just (fromEnum a - 1)
54+ -- | for all a < lastEnum: fromEnum <$> succ a = Just (fromEnum a + 1)
55+
56+
3857 class (Ord a ) <= Enum a where
3958 cardinality :: Cardinality a
4059
@@ -46,16 +65,62 @@ module Data.Enum
4665
4766 pred :: a -> Maybe a
4867
49- toEnum :: forall a. (Enum a ) => Number -> Maybe a
50- toEnum n | n < 0 = Nothing
51- toEnum 0 = Just firstEnum
52- toEnum n = toEnum (n - 1) >>= succ
53-
54- fromEnum :: forall a. (Enum a ) => a -> Number
55- fromEnum e = maybe 0 ((+) 1 <<< fromEnum ) (pred e )
56-
57- maybeCardinality :: forall a. (Enum a ) => Cardinality a -> Cardinality (Maybe a )
58- maybeCardinality c = Cardinality $ 1 + (runCardinality c )
68+ toEnum :: Number -> Maybe a
69+
70+ fromEnum :: a -> Number
71+
72+ -- | defaultSucc toEnum fromEnum = succ
73+ defaultSucc :: forall a. (Number -> Maybe a ) -> (a -> Number ) -> (a -> Maybe a )
74+ defaultSucc toEnum' fromEnum' a = toEnum' (fromEnum' a + 1)
75+
76+ -- | defaultPred toEnum fromEnum = pred
77+ defaultPred :: forall a. (Number -> Maybe a ) -> (a -> Number ) -> (a -> Maybe a )
78+ defaultPred toEnum' fromEnum' a = toEnum' (fromEnum' a - 1)
79+
80+ -- | Runs in O(n) where n is (fromEnum a)
81+ -- | defaultToEnum succ firstEnum = toEnum
82+ defaultToEnum :: forall a. (a -> Maybe a ) -> a -> (Number -> Maybe a )
83+ defaultToEnum succ' firstEnum' n | n < 0 = Nothing
84+ defaultToEnum succ' firstEnum' 0 = Just firstEnum'
85+ defaultToEnum succ' firstEnum' n = defaultToEnum succ' firstEnum' (n - 1) >>= succ'
86+
87+ -- | Runs in O(n) where n is (fromEnum a)
88+ -- | defaultFromEnum pred = fromEnum
89+ defaultFromEnum :: forall a. (a -> Maybe a ) -> (a -> Number )
90+ defaultFromEnum pred' e = maybe 0 (\prd -> defaultFromEnum pred' prd + 1) (pred' e )
91+
92+ -- Property: fromEnum a = a', fromEnum b = b' => forall e', a' <= e' <= b': Exists e: toEnum e' = Just e
93+ -- Following from the propery of intFromTo, We are sure all elements in intFromTo (fromEnum a) (fromEnum b) are Justs.
94+ enumFromTo :: forall a. (Enum a ) => a -> a -> [a]
95+ enumFromTo a b = (toEnum >>> fromJust ) <$> intFromTo a' b'
96+ where a' = fromEnum a
97+ b' = fromEnum b
98+
99+ -- [a,b..c]
100+ -- Correctness for using fromJust is the same as for enumFromTo.
101+ enumFromThenTo :: forall a. (Enum a ) => a -> a -> a -> [a]
102+ enumFromThenTo a b c = (toEnum >>> fromJust ) <$> intStepFromTo (b' - a' ) a' c'
103+ where a' = fromEnum a
104+ b' = fromEnum b
105+ c' = fromEnum c
106+
107+ -- Property: forall e in intFromTo a b: a <= e <= b
108+ -- intFromTo :: Int -> Int -> List Int
109+ intFromTo :: Number -> Number -> [Number ]
110+ intFromTo = intStepFromTo 1
111+
112+ -- Property: forall e in intStepFromTo step a b: a <= e <= b
113+ -- intStepFromTo :: Int -> Int -> Int -> List Int
114+ intStepFromTo :: Number -> Number -> Number -> [Number ]
115+ intStepFromTo step from to =
116+ unfoldr (\e ->
117+ if e <= to
118+ then Just $ Tuple e (e + step) -- Output the value e, set the next state to (e + step)
119+ else Nothing -- End of the collection.
120+ ) from -- starting value/state.
121+
122+
123+ -- | Instances
59124
60125 instance enumChar :: Enum Char where
61126 cardinality = Cardinality (65535 + 1)
@@ -64,9 +129,21 @@ module Data.Enum
64129
65130 lastEnum = fromCharCode 65535
66131
67- succ c = if c == last Enum then Nothing else Just $ ( fromCharCode <<< ((+) 1) <<< to CharCode ) c
132+ succ = default Succ char ToEnum char FromEnum
68133
69- pred c = if c == firstEnum then Nothing else Just $ (fromCharCode <<< ((+) (-1)) <<< toCharCode ) c
134+ pred = defaultPred charToEnum charFromEnum
135+
136+ toEnum = charToEnum
137+
138+ fromEnum = charFromEnum
139+
140+ -- To avoid a compiler bug - can't pass self-class functions, workaround: need to make a concrete function.
141+ charToEnum :: Number -> Maybe Char
142+ charToEnum n | n >= 0 && n <= 65535 = Just $ fromCharCode n
143+ charToEnum _ = Nothing
144+
145+ charFromEnum :: Char -> Number
146+ charFromEnum = toCharCode
70147
71148 instance enumMaybe :: (Enum a ) => Enum (Maybe a ) where
72149 cardinality = maybeCardinality cardinality
@@ -81,18 +158,50 @@ module Data.Enum
81158 pred Nothing = Nothing
82159 pred (Just a ) = Just <$> pred a
83160
161+ toEnum = maybeToEnum cardinality
162+
163+ fromEnum Nothing = 0
164+ fromEnum (Just e ) = fromEnum e + 1
165+
166+ maybeToEnum :: forall a. (Enum a ) => Cardinality a -> Number -> Maybe (Maybe a )
167+ maybeToEnum carda n | n <= runCardinality (maybeCardinality carda ) =
168+ if n == 0
169+ then Just $ Nothing
170+ else Just $ toEnum (n - 1)
171+ maybeToEnum _ _ = Nothing
172+
173+ maybeCardinality :: forall a. (Enum a ) => Cardinality a -> Cardinality (Maybe a )
174+ maybeCardinality c = Cardinality $ 1 + (runCardinality c )
175+
84176 instance enumBoolean :: Enum Boolean where
85177 cardinality = Cardinality 2
86178
87- firstEnum = false
179+ firstEnum = boolean FirstEnum
88180
89181 lastEnum = true
90182
91- succ false = Just true
92- succ _ = Nothing
183+ succ = booleanSucc
184+
185+ pred = booleanPred
186+
187+ toEnum = defaultToEnum booleanSucc booleanFirstEnum
188+
189+ fromEnum = defaultFromEnum booleanPred
190+
191+ booleanFirstEnum :: Boolean
192+ booleanFirstEnum = false
193+
194+ booleanSucc :: Boolean -> Maybe Boolean
195+ booleanSucc false = Just true
196+ booleanSucc _ = Nothing
197+
198+ booleanPred :: Boolean -> Maybe Boolean
199+ booleanPred true = Just false
200+ booleanPred _ = Nothing
93201
94- pred true = Just false
95- pred _ = Nothing
202+ -- Until we get Int, floor and div in the prelude
203+ foreign import floor " function floor(n){ return Math.floor(n); }" :: Number -> Number
204+ div a b = floor (a / b )
96205
97206 instance enumTuple :: (Enum a , Enum b ) => Enum (Tuple a b ) where
98207 cardinality = tupleCardinality cardinality cardinality
@@ -105,6 +214,17 @@ module Data.Enum
105214
106215 pred (Tuple a b ) = maybe (flip Tuple firstEnum <$> pred a ) (Just <<< Tuple a ) (pred b )
107216
217+ toEnum = tupleToEnum cardinality
218+
219+ fromEnum = tupleFromEnum cardinality
220+
221+ -- All of these are as a workaround for ScopedTypeVariables. (not yet supported in Purescript)
222+ tupleToEnum :: forall a b. (Enum a , Enum b ) => Cardinality b -> Number -> Maybe (Tuple a b )
223+ tupleToEnum cardb n = Tuple <$> (toEnum (n `div` (runCardinality cardb))) <*> (toEnum (n % (runCardinality cardb)))
224+
225+ tupleFromEnum :: forall a b. (Enum a , Enum b ) => Cardinality b -> Tuple a b -> Number
226+ tupleFromEnum cardb (Tuple a b ) = (fromEnum a ) * runCardinality cardb + fromEnum b
227+
108228 tupleCardinality :: forall a b. (Enum a , Enum b ) => Cardinality a -> Cardinality b -> Cardinality (Tuple a b )
109229 tupleCardinality l r = Cardinality $ (runCardinality l ) * (runCardinality r )
110230
@@ -121,5 +241,21 @@ module Data.Enum
121241 pred (Left a ) = maybe (Nothing ) (Just <<< Left ) (pred a )
122242 pred (Right b ) = maybe (Just $ Left lastEnum ) (Just <<< Right ) (pred b )
123243
244+ toEnum = eitherToEnum cardinality cardinality
245+
246+ fromEnum = eitherFromEnum cardinality
247+
248+ eitherToEnum :: forall a b. (Enum a , Enum b ) => Cardinality a -> Cardinality b -> Number -> Maybe (Either a b )
249+ eitherToEnum carda cardb n =
250+ if n >= 0 && n < runCardinality carda
251+ then Left <$> toEnum n
252+ else if n >= (runCardinality carda ) && n < runCardinality (eitherCardinality carda cardb )
253+ then Right <$> toEnum (n - runCardinality carda )
254+ else Nothing
255+
256+ eitherFromEnum :: forall a b. (Enum a , Enum b ) => Cardinality a -> (Either a b -> Number )
257+ eitherFromEnum carda (Left a ) = fromEnum a
258+ eitherFromEnum carda (Right b ) = fromEnum b + runCardinality carda
259+
124260 eitherCardinality :: forall a b. (Enum a , Enum b ) => Cardinality a -> Cardinality b -> Cardinality (Either a b )
125- eitherCardinality l r = Cardinality $ (runCardinality l ) + (runCardinality r )
261+ eitherCardinality l r = Cardinality $ (runCardinality l ) + (runCardinality r )
0 commit comments