Skip to content
This repository was archived by the owner on Jun 15, 2023. It is now read-only.

Commit 408c57c

Browse files
committed
Introduce EJsonMap type
By only including special case Eq/Ord/Functor code for this type the rest of the code for EJsonF instances is less error prone
1 parent 1448759 commit 408c57c

File tree

7 files changed

+76
-143
lines changed

7 files changed

+76
-143
lines changed

src/Data/Json/Extended.purs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,10 @@ array ∷ ∀ t. Corecursive t Sig.EJsonF ⇒ Array t → t
146146
array = embed <<< Sig.Array
147147

148148
map t. Corecursive t Sig.EJsonF Map.Map t t t
149-
map = embed <<< Sig.Map <<< A.fromFoldable <<< Map.toList
149+
map = embed <<< Sig.Map <<< Sig.EJsonMap <<< A.fromFoldable <<< Map.toList
150150

151151
map' t. Corecursive t Sig.EJsonF SM.StrMap t t
152-
map' = embed <<< Sig.Map <<< F.map go <<< A.fromFoldable <<< SM.toList
152+
map' = embed <<< Sig.Map <<< Sig.EJsonMap <<< F.map go <<< A.fromFoldable <<< SM.toList
153153
where
154154
go (T.Tuple a b) = T.Tuple (string a) b
155155

@@ -213,10 +213,10 @@ _Array = prism' array $ project >>> case _ of
213213

214214
_Map t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF, Ord t) Prism' t (Map.Map t t)
215215
_Map = prism' map $ project >>> case _ of
216-
Sig.Map kvs → M.Just $ Map.fromFoldable kvs
216+
Sig.Map (Sig.EJsonMap kvs)M.Just $ Map.fromFoldable kvs
217217
_ → M.Nothing
218218

219219
_Map' t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t (SM.StrMap t)
220220
_Map' = prism' map' $ project >>> case _ of
221-
Sig.Map kvs → SM.fromFoldable <$> for kvs (bitraverse (preview _String) pure)
221+
Sig.Map (Sig.EJsonMap kvs)SM.fromFoldable <$> for kvs (bitraverse (preview _String) pure)
222222
_ → M.Nothing

src/Data/Json/Extended/Cursor.purs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ set cur x v = case lmap project <$> peel cur of
105105
-- | ```
106106
getKey EJ.EJson EJ.EJson Maybe EJ.EJson
107107
getKey k v = case project v of
108-
EJ.Map fields → lookup k fields
108+
EJ.Map (EJ.EJsonMap fields) → lookup k fields
109109
_ → Nothing
110110

111111
-- | For a given key, attempts to set a new value for it in an EJson Map. If the
@@ -120,8 +120,8 @@ getKey k v = case project v of
120120
-- | ```
121121
setKey EJ.EJson EJ.EJson EJ.EJson EJ.EJson
122122
setKey k x v = case project v of
123-
EJ.Map fields →
124-
embed <<< EJ.Map $ map
123+
EJ.Map (EJ.EJsonMap fields)
124+
embed <<< EJ.Map <<< EJ.EJsonMap $ map
125125
(\(kv@(Tuple k' v)) → if k == k' then Tuple k x else kv) fields
126126
_ → v
127127

Lines changed: 56 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
module Data.Json.Extended.Signature.Core
22
( EJsonF(..)
3+
, EJsonMap(..)
34
, getType
45
) where
56

67
import Prelude
78

89
import Data.Bifunctor as BF
910
import Data.DateTime as DT
10-
import Data.Eq (class Eq1, eq1)
11+
import Data.Eq (class Eq1)
1112
import Data.Foldable as F
1213
import Data.HugeNum as HN
13-
import Data.Int as Int
1414
import Data.Json.Extended.Type as JT
1515
import Data.List as L
1616
import Data.Map as M
1717
import Data.Monoid (mempty)
18-
import Data.Ord (class Ord1, compare1)
18+
import Data.Newtype (class Newtype)
19+
import Data.Ord (class Ord1)
1920
import Data.TacitString (TacitString)
2021
import Data.Traversable as T
2122
import Data.Tuple (Tuple(..))
@@ -33,42 +34,34 @@ data EJsonF a
3334
| Interval String
3435
| ObjectId String
3536
| Array (Array a)
36-
| Map (Array (Tuple a a))
37-
38-
instance functorEJsonFFunctor EJsonF where
39-
map f x =
40-
case x of
41-
NullNull
42-
String str → String str
43-
Boolean b → Boolean b
44-
Integer i → Integer i
45-
Decimal a → Decimal a
46-
Timestamp ts → Timestamp ts
47-
Date d → Date d
48-
Time t → Time t
49-
Interval i → Interval i
50-
ObjectId oid → ObjectId oid
51-
Array xs → Array $ f <$> xs
52-
Map xs → Map $ BF.bimap f f <$> xs
37+
| Map (EJsonMap a)
38+
39+
derive instance functorEJsonFFunctor EJsonF
40+
41+
derive instance eqEJsonFEq a Eq (EJsonF a)
42+
instance eq1EJsonFEq1 EJsonF where eq1 = eq
43+
44+
derive instance ordEJsonFOrd a Ord (EJsonF a)
45+
instance ord1EJsonFOrd1 EJsonF where compare1 = compare
5346

5447
instance foldableEJsonFF.Foldable EJsonF where
5548
foldMap f = case _ of
5649
Array xs → F.foldMap f xs
57-
Map xs → F.foldMap (\(Tuple k v) → f k <> f v) xs
50+
Map xs → F.foldMap f xs
5851
_ → mempty
5952
foldl f a = case _ of
6053
Array xs → F.foldl f a xs
61-
Map xs → F.foldl (\acc (Tuple k v) → f (f acc k) v) a xs
54+
Map xs → F.foldl f a xs
6255
_ → a
6356
foldr f a = case _ of
6457
Array xs → F.foldr f a xs
65-
Map xs → F.foldr (\(Tuple k v) acc → f k $ f v acc) a xs
58+
Map xs → F.foldr f a xs
6659
_ → a
6760

6861
instance traversableEJsonFT.Traversable EJsonF where
6962
traverse f = case _ of
70-
Array xs → map Array $ T.traverse f xs
71-
Map xs → map Map $ T.traverse (\(Tuple k v) → Tuple <$> f k <*> f v) xs
63+
Array xs → Array <$> T.traverse f xs
64+
Map xs → Map <$> T.traverse f xs
7265
Null → pure Null
7366
String str → pure $ String str
7467
Boolean b → pure $ Boolean b
@@ -81,108 +74,6 @@ instance traversableEJsonF ∷ T.Traversable EJsonF where
8174
ObjectId oid → pure $ ObjectId oid
8275
sequence = T.sequenceDefault
8376

84-
-- Note: this cannot be derived due to integer/decimal equality and map equality
85-
instance eq1EJsonFEq1 EJsonF where
86-
eq1 Null Null = true
87-
eq1 (Boolean b1) (Boolean b2) = b1 == b2
88-
eq1 (Integer i) (Integer j) = i == j
89-
eq1 (Decimal a) (Decimal b) = a == b
90-
eq1 (Integer i) (Decimal b) = intToHugeNum i == b
91-
eq1 (Decimal a) (Integer j) = a == intToHugeNum j
92-
eq1 (String a) (String b) = a == b
93-
eq1 (Timestamp a) (Timestamp b) = a == b
94-
eq1 (Date a) (Date b) = a == b
95-
eq1 (Time a) (Time b) = a == b
96-
eq1 (Interval a) (Interval b) = a == b
97-
eq1 (ObjectId a) (ObjectId b) = a == b
98-
eq1 (Array xs) (Array ys) = xs == ys
99-
eq1 (Map xs) (Map ys) =
100-
let
101-
xs' = L.fromFoldable xs
102-
ys' = L.fromFoldable ys
103-
in
104-
isSubobject xs' ys'
105-
&& isSubobject ys' xs'
106-
eq1 _ _ = false
107-
108-
instance eqEJsonFEq a Eq (EJsonF a) where
109-
eq = eq1
110-
111-
-- | Very badly performing, but we don't have access to Ord here,
112-
-- | so the performant version is not implementable.
113-
isSubobject
114-
a b
115-
. (Eq a, Eq b)
116-
L.List (Tuple a b)
117-
L.List (Tuple a b)
118-
Boolean
119-
isSubobject xs ys =
120-
F.foldl
121-
(\acc x → acc && F.elem x ys)
122-
true
123-
xs
124-
125-
intToHugeNum
126-
Int
127-
HN.HugeNum
128-
intToHugeNum =
129-
HN.fromNumber
130-
<<< Int.toNumber
131-
132-
instance ordEJsonFOrd a Ord (EJsonF a) where
133-
compare = compare1
134-
135-
-- Note: this cannot be derived, due to integer/decimal comparisons and map
136-
-- comparisons
137-
instance ord1EJsonFOrd1 EJsonF where
138-
compare1 Null Null = EQ
139-
compare1 _ Null = GT
140-
compare1 Null _ = LT
141-
142-
compare1 (Boolean b1) (Boolean b2) = compare b1 b2
143-
compare1 _ (Boolean _) = GT
144-
compare1 (Boolean _) _ = LT
145-
146-
compare1 (Integer i) (Integer j) = compare i j
147-
compare1 (Integer i) (Decimal b) = compare (intToHugeNum i) b
148-
compare1 (Decimal a) (Integer j) = compare a (intToHugeNum j)
149-
compare1 _ (Integer _) = GT
150-
compare1 (Integer _) _ = LT
151-
152-
compare1 (Decimal a) (Decimal b) = compare a b
153-
compare1 _ (Decimal _) = GT
154-
compare1 (Decimal _) _ = LT
155-
156-
compare1 (String a) (String b) = compare a b
157-
compare1 _ (String _) = GT
158-
compare1 (String _) _ = LT
159-
160-
compare1 (Timestamp a) (Timestamp b) = compare a b
161-
compare1 _ (Timestamp _) = GT
162-
compare1 (Timestamp _) _ = LT
163-
164-
compare1 (Date a) (Date b) = compare a b
165-
compare1 _ (Date _) = GT
166-
compare1 (Date _) _ = LT
167-
168-
compare1 (Time a) (Time b) = compare a b
169-
compare1 _ (Time _) = GT
170-
compare1 (Time _) _ = LT
171-
172-
compare1 (Interval a) (Interval b) = compare a b
173-
compare1 _ (Interval _) = GT
174-
compare1 (Interval _) _ = LT
175-
176-
compare1 (ObjectId a) (ObjectId b) = compare a b
177-
compare1 _ (ObjectId _) = GT
178-
compare1 (ObjectId _) _ = LT
179-
180-
compare1 (Array a) (Array b) = compare a b
181-
compare1 _ (Array _) = GT
182-
compare1 (Array _) _ = LT
183-
184-
compare1 (Map a) (Map b) = compare (M.fromFoldable a) (M.fromFoldable b)
185-
18677
instance showEJsonFShow (EJsonF TacitString) where
18778
show = case _ of
18879
Null"Null"
@@ -212,3 +103,41 @@ getType = case _ of
212103
ObjectId _ → JT.ObjectId
213104
Array _ → JT.Array
214105
Map _ → JT.Map
106+
107+
newtype EJsonMap a = EJsonMap (Array (Tuple a a))
108+
109+
derive instance newtypeEJsonMapNewtype (EJsonMap a) _
110+
111+
instance functorEJsonMapFunctor EJsonMap where
112+
map f (EJsonMap xs) = EJsonMap (BF.bimap f f <$> xs)
113+
114+
instance eqEJsonMapEq a Eq (EJsonMap a) where
115+
eq (EJsonMap xs) (EJsonMap ys) =
116+
let
117+
xs' = L.fromFoldable xs
118+
ys' = L.fromFoldable ys
119+
in
120+
isSubobject xs' ys'
121+
&& isSubobject ys' xs'
122+
123+
-- | Very badly performing, but we don't have access to Ord here,
124+
-- | so the performant version is not implementable.
125+
isSubobject a. Eq a L.List (Tuple a a) L.List (Tuple a a) Boolean
126+
isSubobject xs ys = F.foldl (\acc x → acc && F.elem x ys) true xs
127+
128+
instance ordEJsonMapOrd a Ord (EJsonMap a) where
129+
compare (EJsonMap xs) (EJsonMap ys) =
130+
compare (M.fromFoldable xs) (M.fromFoldable ys)
131+
132+
instance showEJsonMapShow (EJsonMap TacitString) where
133+
show (EJsonMap xs) = "(EJsonMap " <> show xs <> ")"
134+
135+
instance foldableEJsonMapF.Foldable EJsonMap where
136+
foldMap f (EJsonMap xs) = F.foldMap (\(Tuple k v) → f k <> f v) xs
137+
foldl f a (EJsonMap xs) = F.foldl (\acc (Tuple k v) → f (f acc k) v) a xs
138+
foldr f a (EJsonMap xs) = F.foldr (\(Tuple k v) acc → f k $ f v acc) a xs
139+
140+
instance traversableEJsonMapT.Traversable EJsonMap where
141+
traverse f (EJsonMap xs) =
142+
EJsonMap <$> T.traverse (\(Tuple k v) → Tuple <$> f k <*> f v) xs
143+
sequence = T.sequenceDefault

src/Data/Json/Extended/Signature/Gen.purs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Data.Array as A
1010
import Data.DateTime as DT
1111
import Data.Enum (toEnum)
1212
import Data.HugeNum as HN
13-
import Data.Json.Extended.Signature.Core (EJsonF(..))
13+
import Data.Json.Extended.Signature.Core (EJsonF(..), EJsonMap(..))
1414
import Data.Maybe (fromMaybe)
1515
import Data.Tuple as T
1616

@@ -42,7 +42,7 @@ arbitraryEJsonFWithKeyGen keyGen rec =
4242
Gen.oneOf (pure Null)
4343
[ arbitraryBaseEJsonF
4444
, Array <$> Gen.arrayOf rec
45-
, Map <$> do
45+
, Map <<< EJsonMap <$> do
4646
keys ← distinctArrayOf keyGen
4747
vals ← Gen.vectorOf (A.length keys) rec
4848
pure $ A.zip keys vals

src/Data/Json/Extended/Signature/Json.purs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Data.DateTime as DT
1313
import Data.Either as E
1414
import Data.HugeNum as HN
1515
import Data.Int as Int
16-
import Data.Json.Extended.Signature.Core (EJsonF(..))
16+
import Data.Json.Extended.Signature.Core (EJsonF(..), EJsonMap(..))
1717
import Data.Json.Extended.Signature.Parse (parseDate, parseTime, parseTimestamp)
1818
import Data.Json.Extended.Signature.Render (renderDate, renderTime, renderTimestamp)
1919
import Data.Maybe as M
@@ -37,7 +37,7 @@ encodeJsonEJsonF = case _ of
3737
Interval str → JS.jsonSingletonObject "$interval" $ encodeJson str
3838
ObjectId str → JS.jsonSingletonObject "$oid" $ encodeJson str
3939
Array xs → encodeJson xs
40-
Map xsJS.jsonSingletonObject "$obj" $ encodeJson $ asStrMap xs
40+
Map (EJsonMap xs)JS.jsonSingletonObject "$obj" $ encodeJson $ asStrMap xs
4141
where
4242
tuple
4343
T.Tuple JS.Json JS.Json
@@ -89,6 +89,7 @@ decodeJsonEJsonF =
8989
EJsonF JS.Json
9090
strMapObject =
9191
Map
92+
<<< EJsonMap
9293
<<< A.fromFoldable
9394
<<< map (lmap encodeJson)
9495
<<< SM.toList

src/Data/Json/Extended/Signature/Parse.purs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import Data.Enum (toEnum)
1515
import Data.Foldable as F
1616
import Data.HugeNum as HN
1717
import Data.Int as Int
18-
import Data.Json.Extended.Signature.Core (EJsonF(..))
18+
import Data.Json.Extended.Signature.Core (EJsonF(..), EJsonMap(..))
1919
import Data.List as L
2020
import Data.Maybe as M
2121
import Data.String as S
@@ -132,6 +132,9 @@ anyString =
132132
A.many PS.anyChar
133133
<#> S.fromCharArray
134134

135+
parseNull m. Monad m P.ParserT String m Unit
136+
parseNull = PS.string "null" $> unit
137+
135138
parseBoolean
136139
m
137140
. Monad m
@@ -291,7 +294,7 @@ parseEJsonF
291294
P.ParserT String m (EJsonF a)
292295
parseEJsonF rec =
293296
PC.choice $
294-
[ Null <$ PS.string "null"
297+
[ Null <$ parseNull
295298
, Boolean <$> parseBoolean
296299
, Decimal <$> PC.try parseDecimal
297300
, Integer <$> parseInt
@@ -302,7 +305,7 @@ parseEJsonF rec =
302305
, Interval <$> taggedLiteral "INTERVAL" stringInner
303306
, ObjectId <$> taggedLiteral "OID" stringInner
304307
, Array <<< A.fromFoldable <$> squares (commaSep rec)
305-
, Map <<< A.fromFoldable <$> braces (commaSep parseAssignment)
308+
, Map <<< EJsonMap <<< A.fromFoldable <$> braces (commaSep parseAssignment)
306309
]
307310

308311
where

src/Data/Json/Extended/Signature/Render.purs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Data.Either (fromRight)
1212
import Data.Enum (class BoundedEnum, fromEnum)
1313
import Data.Foldable as F
1414
import Data.HugeNum as HN
15-
import Data.Json.Extended.Signature.Core (EJsonF(..))
15+
import Data.Json.Extended.Signature.Core (EJsonF(..), EJsonMap(..))
1616
import Data.String.Regex as RX
1717
import Data.String.Regex.Flags as RXF
1818
import Data.Tuple as T
@@ -34,7 +34,7 @@ renderEJsonF = case _ of
3434
Interval str → tagged "INTERVAL" str
3535
ObjectId str → tagged "OID" str
3636
Array ds → squares $ commaSep ds
37-
Map ds → braces $ renderPairs ds
37+
Map (EJsonMap ds) → braces $ renderPairs ds
3838

3939
tagged String String String
4040
tagged tag str =

0 commit comments

Comments
 (0)