Skip to content

Commit eee0729

Browse files
authored
Merge pull request #971 from haskell/pr-950-deriving-empty
Pr 950 deriving empty
2 parents 98875e0 + e98b90e commit eee0729

File tree

6 files changed

+62
-5
lines changed

6 files changed

+62
-5
lines changed

changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
For the latest version of this document, please see [https://github.com/haskell/aeson/blob/master/changelog.md](https://github.com/haskell/aeson/blob/master/changelog.md).
22

3+
### 2.1.2.0
4+
5+
* Support deriving for empty datatypes (such as `Void` and `V1`)
6+
in `FromJSON` and `ToJSON`.
7+
* Add `To/FromJSONKey Void` instances
8+
39
### 2.1.1.0
410

511
- Add `Data.Aeson.KeyMap.!?` (flipped) alias to `Data.Aeson.KeyMap.lookup`.

src/Data/Aeson/TH.hs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{-# LANGUAGE BangPatterns #-}
22
{-# LANGUAGE CPP #-}
3+
{-# LANGUAGE EmptyCase #-}
34
{-# LANGUAGE FlexibleInstances #-}
45
{-# LANGUAGE NamedFieldPuns #-}
56
{-# LANGUAGE NoImplicitPrelude #-}
@@ -75,6 +76,9 @@ Please note that you can derive instances for tuples using the following syntax:
7576
$('deriveJSON' 'defaultOptions' ''(,,,))
7677
@
7778
79+
If you derive `ToJSON` for a type that has no constructors, the splice will
80+
require enabling @EmptyCase@ to compile.
81+
7882
-}
7983
module Data.Aeson.TH
8084
(
@@ -327,8 +331,8 @@ consToValue :: ToJSONFun
327331
-- ^ Constructors for which to generate JSON generating code.
328332
-> Q Exp
329333

330-
consToValue _ _ _ _ [] = error $ "Data.Aeson.TH.consToValue: "
331-
++ "Not a single constructor given!"
334+
consToValue _ _ _ _ [] =
335+
[| \x -> case x of {} |]
332336

333337
consToValue target jc opts instTys cons = autoletE liftSBS $ \letInsert -> do
334338
value <- newName "value"
@@ -688,8 +692,8 @@ consFromJSON :: JSONClass
688692
-- ^ Constructors for which to generate JSON parsing code.
689693
-> Q Exp
690694

691-
consFromJSON _ _ _ _ [] = error $ "Data.Aeson.TH.consFromJSON: "
692-
++ "Not a single constructor given!"
695+
consFromJSON _ _ _ _ [] =
696+
[| \_ -> fail "Attempted to parse empty type" |]
693697

694698
consFromJSON jc tName opts instTys cons = do
695699
value <- newName "value"
@@ -1154,7 +1158,7 @@ instance {-# OVERLAPPABLE #-} LookupField a where
11541158

11551159
instance {-# INCOHERENT #-} LookupField (Maybe a) where
11561160
lookupField pj _ _ = parseOptionalFieldWith pj
1157-
1161+
11581162
#if !MIN_VERSION_base(4,16,0)
11591163
instance {-# INCOHERENT #-} LookupField (Semigroup.Option a) where
11601164
lookupField pj tName rec obj key =

src/Data/Aeson/Types/FromJSON.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,11 @@ class GFromJSON' arity f where
978978
-> Value
979979
-> Parser (f a)
980980

981+
-- | No constructors.
982+
instance GFromJSON' arity V1 where
983+
gParseJSON' _ _ = fail "Attempted to parse empty type"
984+
{-# INLINE gParseJSON' #-}
985+
981986
-- | Single constructor.
982987
instance ( ConsFromJSON arity a
983988
, AllNullary (C1 c a) allNullary
@@ -1537,6 +1542,10 @@ instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
15371542
instance FromJSON Void where
15381543
parseJSON _ = fail "Cannot parse Void"
15391544

1545+
-- | @since 2.1.2.0
1546+
instance FromJSONKey Void where
1547+
fromJSONKey = FromJSONKeyTextParser $ \_ -> fail "Cannot parse Void"
1548+
15401549
instance FromJSON Bool where
15411550
parseJSON (Bool b) = pure b
15421551
parseJSON v = typeMismatch "Bool" v

src/Data/Aeson/Types/ToJSON.hs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{-# LANGUAGE CPP #-}
22
{-# LANGUAGE DefaultSignatures #-}
3+
{-# LANGUAGE EmptyCase #-}
34
{-# LANGUAGE EmptyDataDecls #-}
45
{-# LANGUAGE FlexibleContexts #-}
56
{-# LANGUAGE FlexibleInstances #-}
@@ -829,6 +830,12 @@ instance ( ToJSON1 f
829830
--------------------------------------------------------------------------------
830831
-- Generic toEncoding
831832

833+
instance GToJSON' Encoding arity V1 where
834+
-- Empty values do not exist, which makes the job of formatting them
835+
-- rather easy:
836+
gToJSON _ _ x = case x of {}
837+
{-# INLINE gToJSON #-}
838+
832839
instance ToJSON a => GToJSON' Encoding arity (K1 i a) where
833840
-- Constant values are encoded using their ToJSON instance:
834841
gToJSON _opts _ = toEncoding . unK1
@@ -1306,6 +1313,10 @@ instance ToJSON Void where
13061313
toJSON = absurd
13071314
toEncoding = absurd
13081315

1316+
-- | @since 2.1.2.0
1317+
instance ToJSONKey Void where
1318+
toJSONKey = ToJSONKeyText absurd absurd
1319+
13091320
instance ToJSON Bool where
13101321
toJSON = Bool
13111322
toEncoding = E.bool

tests/Encoders.hs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE EmptyCase #-}
23
{-# LANGUAGE GADTs #-}
34
{-# LANGUAGE NoImplicitPrelude #-}
45
{-# LANGUAGE TemplateHaskell #-}
@@ -408,6 +409,29 @@ thGADTToEncodingDefault = $(mkToEncoding defaultOptions ''GADT)
408409
thGADTParseJSONDefault :: Value -> Parser (GADT String)
409410
thGADTParseJSONDefault = $(mkParseJSON defaultOptions ''GADT)
410411

412+
--------------------------------------------------------------------------------
413+
-- NoConstructors encoders/decoders
414+
--------------------------------------------------------------------------------
415+
416+
thNoConstructorsToJSONDefault :: NoConstructors -> Value
417+
thNoConstructorsToJSONDefault = $(mkToJSON defaultOptions ''NoConstructors)
418+
419+
thNoConstructorsToEncodingDefault :: NoConstructors -> Encoding
420+
thNoConstructorsToEncodingDefault = $(mkToEncoding defaultOptions ''NoConstructors)
421+
422+
thNoConstructorsParseJSONDefault :: Value -> Parser NoConstructors
423+
thNoConstructorsParseJSONDefault = $(mkParseJSON defaultOptions ''NoConstructors)
424+
425+
426+
gNoConstructorsToJSONDefault :: NoConstructors -> Value
427+
gNoConstructorsToJSONDefault = genericToJSON defaultOptions
428+
429+
gNoConstructorsToEncodingDefault :: NoConstructors -> Encoding
430+
gNoConstructorsToEncodingDefault = genericToEncoding defaultOptions
431+
432+
gNoConstructorsParseJSONDefault :: Value -> Parser NoConstructors
433+
gNoConstructorsParseJSONDefault = genericParseJSON defaultOptions
434+
411435
--------------------------------------------------------------------------------
412436
-- OneConstructor encoders/decoders
413437
--------------------------------------------------------------------------------

tests/Types.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ data UFoo = UFoo {
4747
, uFooInt :: Int
4848
} deriving (Show, Eq, Data, Typeable)
4949

50+
data NoConstructors
51+
5052
data OneConstructor = OneConstructor
5153
deriving (Show, Eq, Typeable, Data)
5254

@@ -116,6 +118,7 @@ newtype OptionField = OptionField { optionField :: Option Int }
116118

117119
deriving instance Generic Foo
118120
deriving instance Generic UFoo
121+
deriving instance Generic NoConstructors
119122
deriving instance Generic OneConstructor
120123
deriving instance Generic (Product2 a b)
121124
deriving instance Generic (Product6 a b c d e f)

0 commit comments

Comments
 (0)