Skip to content

Commit 0d35770

Browse files
committed
Merge pull request #6 from NightRa/master
Move toEnum&fromEnum into the Enum class, provide range functions.
2 parents 31b93d0 + 20f822b commit 0d35770

File tree

5 files changed

+185
-34
lines changed

5 files changed

+185
-34
lines changed

Gruntfile.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module.exports = function(grunt) {
1313

1414
pscMake: ["<%=libFiles%>"],
1515
dotPsci: ["<%=libFiles%>"],
16-
docgen: {
16+
pscDocs: {
1717
readme: {
1818
src: "src/**/*.purs",
1919
dest: "README.md"
@@ -37,6 +37,6 @@ module.exports = function(grunt) {
3737
grunt.loadNpmTasks("grunt-contrib-clean");
3838
grunt.loadNpmTasks("grunt-purescript");
3939

40-
grunt.registerTask("make", ["pscMake", "dotPsci", "docgen", "jsvalidate"]);
40+
grunt.registerTask("make", ["pscMake", "dotPsci", "pscDocs", "jsvalidate"]);
4141
grunt.registerTask("default", ["make"]);
4242
};

README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
lastEnum :: a
1717
succ :: a -> Maybe a
1818
pred :: a -> Maybe a
19+
toEnum :: Number -> Maybe a
20+
fromEnum :: a -> Number
1921

2022

2123
### Type Class Instances
@@ -33,8 +35,20 @@
3335

3436
### Values
3537

36-
fromEnum :: forall a. (Enum a) => a -> Number
38+
defaultFromEnum :: forall a. (a -> Maybe a) -> a -> Number
3739

38-
runCardinality :: forall a. Cardinality a -> Number
40+
defaultPred :: forall a. (Number -> Maybe a) -> (a -> Number) -> a -> Maybe a
3941

40-
toEnum :: forall a. (Enum a) => Number -> Maybe a
42+
defaultSucc :: forall a. (Number -> Maybe a) -> (a -> Number) -> a -> Maybe a
43+
44+
defaultToEnum :: forall a. (a -> Maybe a) -> a -> Number -> Maybe a
45+
46+
enumFromThenTo :: forall a. (Enum a) => a -> a -> a -> [a]
47+
48+
enumFromTo :: forall a. (Enum a) => a -> a -> [a]
49+
50+
intFromTo :: Number -> Number -> [Number]
51+
52+
intStepFromTo :: Number -> Number -> Number -> [Number]
53+
54+
runCardinality :: forall a. Cardinality a -> Number

bower.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
],
2020
"dependencies": {
2121
"purescript-maybe": "~0.2.1",
22-
"purescript-tuples": "~0.2.1",
23-
"purescript-either": "~0.1.3",
24-
"purescript-strings": "~0.4.0"
22+
"purescript-tuples": "~0.2.3",
23+
"purescript-either": "~0.1.4",
24+
"purescript-strings": "~0.4.2",
25+
"purescript-unfoldable": "~0.2.0"
2526
}
2627
}

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"private": true,
33
"dependencies": {
44
"grunt": "~0.4.4",
5-
"grunt-purescript": "~0.5.1",
6-
"grunt-contrib-clean": "~0.5.0",
7-
"grunt-jsvalidate": "~0.2.2"
5+
"grunt-purescript": "~0.6.0",
6+
"grunt-contrib-clean": "~0.6.0",
7+
"grunt-jsvalidate": "~0.2.2"
88
}
99
}

src/Data/Enum.purs

Lines changed: 159 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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 == lastEnum then Nothing else Just $ (fromCharCode <<< ((+) 1) <<< toCharCode) c
132+
succ = defaultSucc charToEnum charFromEnum
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 = booleanFirstEnum
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

Comments
 (0)