Skip to content
This repository was archived by the owner on Mar 25, 2021. It is now read-only.

Commit 7b78a94

Browse files
authored
Merge pull request #14 from purescript/generic-enum
Add classes for Enum deriving
2 parents 0444c79 + aeaa792 commit 7b78a94

File tree

4 files changed

+204
-12
lines changed

4 files changed

+204
-12
lines changed

bower.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
"url": "git://github.com/purescript/purescript-generics-rep.git"
1313
},
1414
"dependencies": {
15-
"purescript-prelude": "^3.0.0",
15+
"purescript-enums": "^3.2.1",
16+
"purescript-foldable-traversable": "^3.0.0",
1617
"purescript-monoid": "^3.0.0",
17-
"purescript-symbols": "^3.0.0",
18-
"purescript-foldable-traversable": "^3.0.0"
18+
"purescript-prelude": "^3.0.0",
19+
"purescript-symbols": "^3.0.0"
1920
},
2021
"devDependencies": {
22+
"purescript-assert": "^3.0.0",
2123
"purescript-console": "^3.0.0"
2224
}
2325
}

src/Data/Generic/Rep/Bounded.purs

+8
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@ module Data.Generic.Rep.Bounded
99

1010
import Data.Generic.Rep
1111

12+
import Data.Bounded (class Bounded, bottom, top)
13+
1214
class GenericBottom a where
1315
genericBottom' :: a
1416

1517
instance genericBottomNoArguments :: GenericBottom NoArguments where
1618
genericBottom' = NoArguments
1719

20+
instance genericBottomArgument :: Bounded a => GenericBottom (Argument a) where
21+
genericBottom' = Argument bottom
22+
1823
instance genericBottomSum :: GenericBottom a => GenericBottom (Sum a b) where
1924
genericBottom' = Inl genericBottom'
2025

@@ -27,6 +32,9 @@ class GenericTop a where
2732
instance genericTopNoArguments :: GenericTop NoArguments where
2833
genericTop' = NoArguments
2934

35+
instance genericTopArgument :: Bounded a => GenericTop (Argument a) where
36+
genericTop' = Argument top
37+
3038
instance genericTopSum :: GenericTop b => GenericTop (Sum a b) where
3139
genericTop' = Inr genericTop'
3240

src/Data/Generic/Rep/Enum.purs

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
module Data.Generic.Rep.Enum where
2+
3+
import Prelude
4+
5+
import Data.Enum (class BoundedEnum, class Enum, Cardinality(..), cardinality, fromEnum, pred, succ, toEnum)
6+
import Data.Generic.Rep (class Generic, Argument(..), Constructor(..), NoArguments(..), Sum(..), from, to)
7+
import Data.Generic.Rep.Bounded (class GenericBottom, class GenericTop, genericBottom', genericTop')
8+
import Data.Maybe (Maybe(..))
9+
import Data.Newtype (unwrap)
10+
11+
class GenericEnum a where
12+
genericPred' :: a -> Maybe a
13+
genericSucc' :: a -> Maybe a
14+
15+
instance genericEnumNoArguments :: GenericEnum NoArguments where
16+
genericPred' _ = Nothing
17+
genericSucc' _ = Nothing
18+
19+
instance genericEnumArgument :: Enum a => GenericEnum (Argument a) where
20+
genericPred' (Argument a) = Argument <$> pred a
21+
genericSucc' (Argument a) = Argument <$> succ a
22+
23+
instance genericEnumConstructor :: GenericEnum a => GenericEnum (Constructor name a) where
24+
genericPred' (Constructor a) = Constructor <$> genericPred' a
25+
genericSucc' (Constructor a) = Constructor <$> genericSucc' a
26+
27+
instance genericEnumSum :: (GenericEnum a, GenericTop a, GenericEnum b, GenericBottom b) => GenericEnum (Sum a b) where
28+
genericPred' = case _ of
29+
Inl a -> Inl <$> genericPred' a
30+
Inr b -> case genericPred' b of
31+
Nothing -> Just (Inl genericTop')
32+
Just b' -> Just (Inr b')
33+
genericSucc' = case _ of
34+
Inl a -> case genericSucc' a of
35+
Nothing -> Just (Inr genericBottom')
36+
Just a' -> Just (Inl a')
37+
Inr b -> Inr <$> genericSucc' b
38+
39+
-- | A `Generic` implementation of the `pred` member from the `Enum` type class.
40+
genericPred :: forall a rep. Generic a rep => GenericEnum rep => a -> Maybe a
41+
genericPred = map to <<< genericPred' <<< from
42+
43+
-- | A `Generic` implementation of the `succ` member from the `Enum` type class.
44+
genericSucc :: forall a rep. Generic a rep => GenericEnum rep => a -> Maybe a
45+
genericSucc = map to <<< genericSucc' <<< from
46+
47+
class GenericBoundedEnum a where
48+
genericCardinality' :: Cardinality a
49+
genericToEnum' :: Int -> Maybe a
50+
genericFromEnum' :: a -> Int
51+
52+
instance genericBoundedEnumNoArguments :: GenericBoundedEnum NoArguments where
53+
genericCardinality' = Cardinality 1
54+
genericToEnum' i = if i == 0 then Just NoArguments else Nothing
55+
genericFromEnum' _ = 0
56+
57+
instance genericBoundedEnumArgument :: BoundedEnum a => GenericBoundedEnum (Argument a) where
58+
genericCardinality' = Cardinality (unwrap (cardinality :: Cardinality a))
59+
genericToEnum' i = Argument <$> toEnum i
60+
genericFromEnum' (Argument a) = fromEnum a
61+
62+
instance genericBoundedEnumConstructor :: GenericBoundedEnum a => GenericBoundedEnum (Constructor name a) where
63+
genericCardinality' = Cardinality (unwrap (genericCardinality' :: Cardinality a))
64+
genericToEnum' i = Constructor <$> genericToEnum' i
65+
genericFromEnum' (Constructor a) = genericFromEnum' a
66+
67+
instance genericBoundedEnumSum :: (GenericBoundedEnum a, GenericBoundedEnum b) => GenericBoundedEnum (Sum a b) where
68+
genericCardinality' =
69+
Cardinality
70+
$ unwrap (genericCardinality' :: Cardinality a)
71+
+ unwrap (genericCardinality' :: Cardinality b)
72+
genericToEnum' n = to genericCardinality'
73+
where
74+
to :: Cardinality a -> Maybe (Sum a b)
75+
to (Cardinality ca)
76+
| n >= 0 && n < ca = Inl <$> genericToEnum' n
77+
| otherwise = Inr <$> genericToEnum' (n - ca)
78+
genericFromEnum' = case _ of
79+
Inl a -> genericFromEnum' a
80+
Inr b -> genericFromEnum' b + unwrap (genericCardinality' :: Cardinality a)
81+
82+
-- | A `Generic` implementation of the `cardinality` member from the
83+
-- | `BoundedEnum` type class.
84+
genericCardinality :: forall a rep. Generic a rep => GenericBoundedEnum rep => Cardinality a
85+
genericCardinality = Cardinality (unwrap (genericCardinality' :: Cardinality rep))
86+
87+
-- | A `Generic` implementation of the `toEnum` member from the `BoundedEnum`
88+
-- | type class.
89+
genericToEnum :: forall a rep. Generic a rep => GenericBoundedEnum rep => Int -> Maybe a
90+
genericToEnum = map to <<< genericToEnum'
91+
92+
-- | A `Generic` implementation of the `fromEnum` member from the `BoundedEnum`
93+
-- | type class.
94+
genericFromEnum :: forall a rep. Generic a rep => GenericBoundedEnum rep => a -> Int
95+
genericFromEnum = genericFromEnum' <<< from

test/Main.purs

+96-9
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
module Test.Main where
22

33
import Prelude
4+
45
import Control.Monad.Eff (Eff)
5-
import Control.Monad.Eff.Console (CONSOLE, logShow)
6+
import Control.Monad.Eff.Console (CONSOLE, log, logShow)
7+
import Data.Enum (class BoundedEnum, class Enum, Cardinality(..), cardinality, fromEnum, pred, succ, toEnum)
68
import Data.Generic.Rep as G
9+
import Data.Generic.Rep.Bounded as GBounded
10+
import Data.Generic.Rep.Enum as GEnum
711
import Data.Generic.Rep.Eq as GEq
812
import Data.Generic.Rep.Ord as GOrd
913
import Data.Generic.Rep.Show as GShow
10-
import Data.Generic.Rep.Bounded as GBounded
14+
import Data.Maybe (Maybe(..))
15+
import Test.Assert (ASSERT, assert)
1116

1217
data List a = Nil | Cons { head :: a, tail :: List a }
1318

@@ -36,16 +41,98 @@ instance showSimpleBounded :: Show SimpleBounded where
3641
instance boundedSimpleBounded :: Bounded SimpleBounded where
3742
bottom = GBounded.genericBottom
3843
top = GBounded.genericTop
44+
instance enumSimpleBounded :: Enum SimpleBounded where
45+
pred = GEnum.genericPred
46+
succ = GEnum.genericSucc
47+
instance boundedEnumSimpleBounded :: BoundedEnum SimpleBounded where
48+
cardinality = GEnum.genericCardinality
49+
toEnum = GEnum.genericToEnum
50+
fromEnum = GEnum.genericFromEnum
51+
52+
data Option a = None | Some a
53+
derive instance genericOption :: G.Generic (Option a) _
54+
instance eqOption :: Eq a => Eq (Option a) where
55+
eq x y = GEq.genericEq x y
56+
instance ordOption :: Ord a => Ord (Option a) where
57+
compare x y = GOrd.genericCompare x y
58+
instance showOption :: Show a => Show (Option a) where
59+
show x = GShow.genericShow x
60+
instance boundedOption :: Bounded a => Bounded (Option a) where
61+
bottom = GBounded.genericBottom
62+
top = GBounded.genericTop
63+
instance enumOption :: (Bounded a, Enum a) => Enum (Option a) where
64+
pred = GEnum.genericPred
65+
succ = GEnum.genericSucc
66+
instance boundedEnumOption :: BoundedEnum a => BoundedEnum (Option a) where
67+
cardinality = GEnum.genericCardinality
68+
toEnum = GEnum.genericToEnum
69+
fromEnum = GEnum.genericFromEnum
3970

40-
main :: Eff (console :: CONSOLE) Unit
71+
main :: Eff (console :: CONSOLE, assert :: ASSERT) Unit
4172
main = do
4273
logShow (cons 1 (cons 2 Nil))
4374

44-
logShow (cons 1 (cons 2 Nil) == cons 1 (cons 2 Nil))
45-
logShow (cons 1 (cons 2 Nil) == cons 1 Nil)
75+
log "Checking equality"
76+
assert $ cons 1 (cons 2 Nil) == cons 1 (cons 2 Nil)
77+
78+
log "Checking inequality"
79+
assert $ cons 1 (cons 2 Nil) /= cons 1 Nil
80+
81+
log "Checking comparison EQ"
82+
assert $ (cons 1 (cons 2 Nil) `compare` cons 1 (cons 2 Nil)) == EQ
83+
84+
log "Checking comparison GT"
85+
assert $ (cons 1 (cons 2 Nil) `compare` cons 1 Nil) == GT
86+
87+
log "Checking comparison LT"
88+
assert $ (cons 1 Nil `compare` cons 1 (cons 2 Nil)) == LT
89+
90+
log "Checking simple bottom"
91+
assert $ bottom == A
92+
93+
log "Checking simple top"
94+
assert $ top == D
95+
96+
log "Checking composite bottom"
97+
assert $ bottom == None :: Option SimpleBounded
98+
99+
log "Checking composite top"
100+
assert $ top == Some D
101+
102+
log "Checking simple pred bottom"
103+
assert $ pred (bottom :: SimpleBounded) == Nothing
104+
105+
log "Checking simple (pred =<< succ bottom)"
106+
assert $ (pred =<< succ bottom) == Just A
107+
108+
log "Checking simple succ top"
109+
assert $ succ (top :: SimpleBounded) == Nothing
110+
111+
log "Checking simple (succ =<< pred top)"
112+
assert $ (succ =<< pred top) == Just D
113+
114+
log "Checking composite pred bottom"
115+
assert $ pred (bottom :: Option SimpleBounded) == Nothing
116+
117+
log "Checking composite (pred =<< succ bottom)"
118+
assert $ (pred =<< succ (bottom :: Option SimpleBounded)) == Just None
119+
120+
log "Checking composite succ top"
121+
assert $ succ (top :: Option SimpleBounded) == Nothing
122+
123+
log "Checking composite (succ =<< pred top)"
124+
assert $ (succ =<< pred top) == Just (Some D)
125+
126+
log "Checking simple cardinality"
127+
assert $ (cardinality :: Cardinality SimpleBounded) == Cardinality 4
128+
129+
log "Checking composite cardinality"
130+
assert $ (cardinality :: Cardinality (Option SimpleBounded)) == Cardinality 5
46131

47-
logShow (cons 1 (cons 2 Nil) `compare` cons 1 (cons 2 Nil))
48-
logShow (cons 1 (cons 2 Nil) `compare` cons 1 Nil)
132+
log "Checking simple toEnum/fromEnum roundtrip"
133+
assert $ toEnum (fromEnum A) == Just A
134+
assert $ toEnum (fromEnum B) == Just B
49135

50-
logShow (bottom :: SimpleBounded)
51-
logShow (top :: SimpleBounded)
136+
log "Checking composite toEnum/fromEnum roundtrip"
137+
assert $ toEnum (fromEnum (None :: Option SimpleBounded)) == Just (None :: Option SimpleBounded)
138+
assert $ toEnum (fromEnum (Some A)) == Just (Some A)

0 commit comments

Comments
 (0)