@@ -9,13 +9,22 @@ module Data.Enum
9
9
, runCardinality
10
10
, succ
11
11
, toEnum
12
+ , defaultSucc
13
+ , defaultPred
14
+ , defaultToEnum
15
+ , defaultFromEnum
16
+ , intFromTo
17
+ , intStepFromTo
18
+ , enumFromTo
19
+ , enumFromThenTo
12
20
) where
13
21
14
22
import Data.Maybe
15
23
import Data.Either
16
24
import Data.Tuple
17
25
import Data.Char
18
26
import Data.Maybe.Unsafe
27
+ import Data.Unfoldable
19
28
20
29
newtype Cardinality a = Cardinality Number
21
30
@@ -28,13 +37,23 @@ module Data.Enum
28
37
-- | to easily compute successor and predecessor elements. e.g. DayOfWeek, etc.
29
38
-- |
30
39
-- | 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
+ -- |
36
45
-- | for all a > firstEnum: pred a >>= succ == Just a
37
46
-- | 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
+
38
57
class (Ord a ) <= Enum a where
39
58
cardinality :: Cardinality a
40
59
@@ -46,16 +65,62 @@ module Data.Enum
46
65
47
66
pred :: a -> Maybe a
48
67
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
59
124
60
125
instance enumChar :: Enum Char where
61
126
cardinality = Cardinality (65535 + 1)
@@ -64,9 +129,21 @@ module Data.Enum
64
129
65
130
lastEnum = fromCharCode 65535
66
131
67
- succ c = if c == last Enum then Nothing else Just $ ( fromCharCode <<< ((+) 1) <<< to CharCode ) c
132
+ succ = default Succ char ToEnum char FromEnum
68
133
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
70
147
71
148
instance enumMaybe :: (Enum a ) => Enum (Maybe a ) where
72
149
cardinality = maybeCardinality cardinality
@@ -81,18 +158,50 @@ module Data.Enum
81
158
pred Nothing = Nothing
82
159
pred (Just a ) = Just <$> pred a
83
160
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
+
84
176
instance enumBoolean :: Enum Boolean where
85
177
cardinality = Cardinality 2
86
178
87
- firstEnum = false
179
+ firstEnum = boolean FirstEnum
88
180
89
181
lastEnum = true
90
182
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
93
201
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 )
96
205
97
206
instance enumTuple :: (Enum a , Enum b ) => Enum (Tuple a b ) where
98
207
cardinality = tupleCardinality cardinality cardinality
@@ -105,6 +214,17 @@ module Data.Enum
105
214
106
215
pred (Tuple a b ) = maybe (flip Tuple firstEnum <$> pred a ) (Just <<< Tuple a ) (pred b )
107
216
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
+
108
228
tupleCardinality :: forall a b. (Enum a , Enum b ) => Cardinality a -> Cardinality b -> Cardinality (Tuple a b )
109
229
tupleCardinality l r = Cardinality $ (runCardinality l ) * (runCardinality r )
110
230
@@ -121,5 +241,21 @@ module Data.Enum
121
241
pred (Left a ) = maybe (Nothing ) (Just <<< Left ) (pred a )
122
242
pred (Right b ) = maybe (Just $ Left lastEnum ) (Just <<< Right ) (pred b )
123
243
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
+
124
260
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