Blame Data/Aeson/TH.hs

Packit 9a2dfb
{-# LANGUAGE BangPatterns #-}
Packit 9a2dfb
{-# LANGUAGE CPP #-}
Packit 9a2dfb
{-# LANGUAGE FlexibleInstances #-}
Packit 9a2dfb
{-# LANGUAGE NamedFieldPuns #-}
Packit 9a2dfb
{-# LANGUAGE NoImplicitPrelude #-}
Packit 9a2dfb
{-# LANGUAGE UndecidableInstances #-}
Packit 9a2dfb
#if __GLASGOW_HASKELL__ >= 800
Packit 9a2dfb
-- a) THQ works on cross-compilers and unregisterised GHCs
Packit 9a2dfb
-- b) may make compilation faster as no dynamic loading is ever needed (not sure about this)
Packit 9a2dfb
-- c) removes one hindrance to have code inferred as SafeHaskell safe
Packit 9a2dfb
{-# LANGUAGE TemplateHaskellQuotes #-}
Packit 9a2dfb
#else
Packit 9a2dfb
{-# LANGUAGE TemplateHaskell #-}
Packit 9a2dfb
#endif
Packit 9a2dfb
Packit 9a2dfb
#include "incoherent-compat.h"
Packit 9a2dfb
#include "overlapping-compat.h"
Packit 9a2dfb
Packit 9a2dfb
{-|
Packit 9a2dfb
Module:      Data.Aeson.TH
Packit 9a2dfb
Copyright:   (c) 2011-2016 Bryan O'Sullivan
Packit 9a2dfb
             (c) 2011 MailRank, Inc.
Packit 9a2dfb
License:     BSD3
Packit 9a2dfb
Stability:   experimental
Packit 9a2dfb
Portability: portable
Packit 9a2dfb
Packit 9a2dfb
Functions to mechanically derive 'ToJSON' and 'FromJSON' instances. Note that
Packit 9a2dfb
you need to enable the @TemplateHaskell@ language extension in order to use this
Packit 9a2dfb
module.
Packit 9a2dfb
Packit 9a2dfb
An example shows how instances are generated for arbitrary data types. First we
Packit 9a2dfb
define a data type:
Packit 9a2dfb
Packit 9a2dfb
@
Packit 9a2dfb
data D a = Nullary
Packit 9a2dfb
         | Unary Int
Packit 9a2dfb
         | Product String Char a
Packit 9a2dfb
         | Record { testOne   :: Double
Packit 9a2dfb
                  , testTwo   :: Bool
Packit 9a2dfb
                  , testThree :: D a
Packit 9a2dfb
                  } deriving Eq
Packit 9a2dfb
@
Packit 9a2dfb
Packit 9a2dfb
Next we derive the necessary instances. Note that we make use of the
Packit 9a2dfb
feature to change record field names. In this case we drop the first 4
Packit 9a2dfb
characters of every field name. We also modify constructor names by
Packit 9a2dfb
lower-casing them:
Packit 9a2dfb
Packit 9a2dfb
@
Packit 9a2dfb
$('deriveJSON' 'defaultOptions'{'fieldLabelModifier' = 'drop' 4, 'constructorTagModifier' = map toLower} ''D)
Packit 9a2dfb
@
Packit 9a2dfb
Packit 9a2dfb
Now we can use the newly created instances.
Packit 9a2dfb
Packit 9a2dfb
@
Packit 9a2dfb
d :: D 'Int'
Packit 9a2dfb
d = Record { testOne = 3.14159
Packit 9a2dfb
           , testTwo = 'True'
Packit 9a2dfb
           , testThree = Product \"test\" \'A\' 123
Packit 9a2dfb
           }
Packit 9a2dfb
@
Packit 9a2dfb
Packit 9a2dfb
>>> fromJSON (toJSON d) == Success d
Packit 9a2dfb
> True
Packit 9a2dfb
Packit 9a2dfb
This also works for data family instances, but instead of passing in the data
Packit 9a2dfb
family name (with double quotes), we pass in a data family instance
Packit 9a2dfb
constructor (with a single quote):
Packit 9a2dfb
Packit 9a2dfb
@
Packit 9a2dfb
data family DF a
Packit 9a2dfb
data instance DF Int = DF1 Int
Packit 9a2dfb
                     | DF2 Int Int
Packit 9a2dfb
                     deriving Eq
Packit 9a2dfb
Packit 9a2dfb
$('deriveJSON' 'defaultOptions' 'DF1)
Packit 9a2dfb
-- Alternatively, one could pass 'DF2 instead
Packit 9a2dfb
@
Packit 9a2dfb
Packit 9a2dfb
Please note that you can derive instances for tuples using the following syntax:
Packit 9a2dfb
Packit 9a2dfb
@
Packit 9a2dfb
-- FromJSON and ToJSON instances for 4-tuples.
Packit 9a2dfb
$('deriveJSON' 'defaultOptions' ''(,,,))
Packit 9a2dfb
@
Packit 9a2dfb
Packit 9a2dfb
-}
Packit 9a2dfb
module Data.Aeson.TH
Packit 9a2dfb
    (
Packit 9a2dfb
      -- * Encoding configuration
Packit 9a2dfb
      Options(..)
Packit 9a2dfb
    , SumEncoding(..)
Packit 9a2dfb
    , defaultOptions
Packit 9a2dfb
    , defaultTaggedObject
Packit 9a2dfb
Packit 9a2dfb
     -- * FromJSON and ToJSON derivation
Packit 9a2dfb
    , deriveJSON
Packit 9a2dfb
    , deriveJSON1
Packit 9a2dfb
    , deriveJSON2
Packit 9a2dfb
Packit 9a2dfb
    , deriveToJSON
Packit 9a2dfb
    , deriveToJSON1
Packit 9a2dfb
    , deriveToJSON2
Packit 9a2dfb
    , deriveFromJSON
Packit 9a2dfb
    , deriveFromJSON1
Packit 9a2dfb
    , deriveFromJSON2
Packit 9a2dfb
Packit 9a2dfb
    , mkToJSON
Packit 9a2dfb
    , mkLiftToJSON
Packit 9a2dfb
    , mkLiftToJSON2
Packit 9a2dfb
    , mkToEncoding
Packit 9a2dfb
    , mkLiftToEncoding
Packit 9a2dfb
    , mkLiftToEncoding2
Packit 9a2dfb
    , mkParseJSON
Packit 9a2dfb
    , mkLiftParseJSON
Packit 9a2dfb
    , mkLiftParseJSON2
Packit 9a2dfb
    ) where
Packit 9a2dfb
Packit 9a2dfb
import Prelude ()
Packit 9a2dfb
import Prelude.Compat hiding (exp)
Packit 9a2dfb
Packit 9a2dfb
import Control.Applicative ((<|>))
Packit 9a2dfb
import Data.Aeson (Object, (.=), (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
Packit 9a2dfb
import Data.Aeson.Types (Options(..), Parser, SumEncoding(..), Value(..), defaultOptions, defaultTaggedObject)
Packit 9a2dfb
import Data.Aeson.Types.Internal ((), JSONPathElement(Key))
Packit 9a2dfb
import Data.Aeson.Types.FromJSON (parseOptionalFieldWith)
Packit 9a2dfb
import Control.Monad (liftM2, unless, when)
Packit 9a2dfb
import Data.Foldable (foldr')
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,8,0) && !MIN_VERSION_template_haskell(2,10,0)
Packit 9a2dfb
import Data.List (nub)
Packit 9a2dfb
#endif
Packit 9a2dfb
import Data.List (foldl', genericLength, intercalate, partition, union)
Packit 9a2dfb
import Data.List.NonEmpty ((<|), NonEmpty((:|)))
Packit 9a2dfb
import Data.Map (Map)
Packit 9a2dfb
import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
Packit 9a2dfb
import Data.Set (Set)
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,8,0)
Packit 9a2dfb
import Language.Haskell.TH hiding (Arity)
Packit 9a2dfb
#else
Packit 9a2dfb
import Language.Haskell.TH
Packit 9a2dfb
#endif
Packit 9a2dfb
import Language.Haskell.TH.Datatype
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,7,0) && !(MIN_VERSION_template_haskell(2,8,0))
Packit 9a2dfb
import Language.Haskell.TH.Lib (starK)
Packit 9a2dfb
#endif
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,8,0) && !(MIN_VERSION_template_haskell(2,10,0))
Packit 9a2dfb
import Language.Haskell.TH.Syntax (mkNameG_tc)
Packit 9a2dfb
#endif
Packit 9a2dfb
import Text.Printf (printf)
Packit 9a2dfb
import qualified Data.Aeson as A
Packit 9a2dfb
import qualified Data.Aeson.Encoding.Internal as E
Packit 9a2dfb
import qualified Data.Foldable as F (all)
Packit 9a2dfb
import qualified Data.HashMap.Strict as H (lookup, toList)
Packit 9a2dfb
import qualified Data.List.NonEmpty as NE (length, reverse)
Packit 9a2dfb
import qualified Data.Map as M (fromList, keys, lookup , singleton, size)
Packit 9a2dfb
import qualified Data.Semigroup as Semigroup (Option(..))
Packit 9a2dfb
import qualified Data.Set as Set (empty, insert, member)
Packit 9a2dfb
import qualified Data.Text as T (Text, pack, unpack)
Packit 9a2dfb
import qualified Data.Vector as V (unsafeIndex, null, length, create, empty)
Packit 9a2dfb
import qualified Data.Vector.Mutable as VM (unsafeNew, unsafeWrite)
Packit 9a2dfb
Packit 9a2dfb
{-# ANN module "Hlint: ignore Reduce duplication" #-}
Packit 9a2dfb
Packit 9a2dfb
--------------------------------------------------------------------------------
Packit 9a2dfb
-- Convenience
Packit 9a2dfb
--------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
-- | Generates both 'ToJSON' and 'FromJSON' instance declarations for the given
Packit 9a2dfb
-- data type or data family instance constructor.
Packit 9a2dfb
--
Packit 9a2dfb
-- This is a convienience function which is equivalent to calling both
Packit 9a2dfb
-- 'deriveToJSON' and 'deriveFromJSON'.
Packit 9a2dfb
deriveJSON :: Options
Packit 9a2dfb
           -- ^ Encoding options.
Packit 9a2dfb
           -> Name
Packit 9a2dfb
           -- ^ Name of the type for which to generate 'ToJSON' and 'FromJSON'
Packit 9a2dfb
           -- instances.
Packit 9a2dfb
           -> Q [Dec]
Packit 9a2dfb
deriveJSON = deriveJSONBoth deriveToJSON deriveFromJSON
Packit 9a2dfb
Packit 9a2dfb
-- | Generates both 'ToJSON1' and 'FromJSON1' instance declarations for the given
Packit 9a2dfb
-- data type or data family instance constructor.
Packit 9a2dfb
--
Packit 9a2dfb
-- This is a convienience function which is equivalent to calling both
Packit 9a2dfb
-- 'deriveToJSON1' and 'deriveFromJSON1'.
Packit 9a2dfb
deriveJSON1 :: Options
Packit 9a2dfb
            -- ^ Encoding options.
Packit 9a2dfb
            -> Name
Packit 9a2dfb
            -- ^ Name of the type for which to generate 'ToJSON1' and 'FromJSON1'
Packit 9a2dfb
            -- instances.
Packit 9a2dfb
            -> Q [Dec]
Packit 9a2dfb
deriveJSON1 = deriveJSONBoth deriveToJSON1 deriveFromJSON1
Packit 9a2dfb
Packit 9a2dfb
-- | Generates both 'ToJSON2' and 'FromJSON2' instance declarations for the given
Packit 9a2dfb
-- data type or data family instance constructor.
Packit 9a2dfb
--
Packit 9a2dfb
-- This is a convienience function which is equivalent to calling both
Packit 9a2dfb
-- 'deriveToJSON2' and 'deriveFromJSON2'.
Packit 9a2dfb
deriveJSON2 :: Options
Packit 9a2dfb
            -- ^ Encoding options.
Packit 9a2dfb
            -> Name
Packit 9a2dfb
            -- ^ Name of the type for which to generate 'ToJSON2' and 'FromJSON2'
Packit 9a2dfb
            -- instances.
Packit 9a2dfb
            -> Q [Dec]
Packit 9a2dfb
deriveJSON2 = deriveJSONBoth deriveToJSON2 deriveFromJSON2
Packit 9a2dfb
Packit 9a2dfb
--------------------------------------------------------------------------------
Packit 9a2dfb
-- ToJSON
Packit 9a2dfb
--------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
{-
Packit 9a2dfb
TODO: Don't constrain phantom type variables.
Packit 9a2dfb
Packit 9a2dfb
data Foo a = Foo Int
Packit 9a2dfb
instance (ToJSON a) ⇒ ToJSON Foo where ...
Packit 9a2dfb
Packit 9a2dfb
The above (ToJSON a) constraint is not necessary and perhaps undesirable.
Packit 9a2dfb
-}
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a 'ToJSON' instance declaration for the given data type or
Packit 9a2dfb
-- data family instance constructor.
Packit 9a2dfb
deriveToJSON :: Options
Packit 9a2dfb
             -- ^ Encoding options.
Packit 9a2dfb
             -> Name
Packit 9a2dfb
             -- ^ Name of the type for which to generate a 'ToJSON' instance
Packit 9a2dfb
             -- declaration.
Packit 9a2dfb
             -> Q [Dec]
Packit 9a2dfb
deriveToJSON = deriveToJSONCommon toJSONClass
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a 'ToJSON1' instance declaration for the given data type or
Packit 9a2dfb
-- data family instance constructor.
Packit 9a2dfb
deriveToJSON1 :: Options
Packit 9a2dfb
              -- ^ Encoding options.
Packit 9a2dfb
              -> Name
Packit 9a2dfb
              -- ^ Name of the type for which to generate a 'ToJSON1' instance
Packit 9a2dfb
              -- declaration.
Packit 9a2dfb
              -> Q [Dec]
Packit 9a2dfb
deriveToJSON1 = deriveToJSONCommon toJSON1Class
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a 'ToJSON2' instance declaration for the given data type or
Packit 9a2dfb
-- data family instance constructor.
Packit 9a2dfb
deriveToJSON2 :: Options
Packit 9a2dfb
              -- ^ Encoding options.
Packit 9a2dfb
              -> Name
Packit 9a2dfb
              -- ^ Name of the type for which to generate a 'ToJSON2' instance
Packit 9a2dfb
              -- declaration.
Packit 9a2dfb
              -> Q [Dec]
Packit 9a2dfb
deriveToJSON2 = deriveToJSONCommon toJSON2Class
Packit 9a2dfb
Packit 9a2dfb
deriveToJSONCommon :: JSONClass
Packit 9a2dfb
                   -- ^ The ToJSON variant being derived.
Packit 9a2dfb
                   -> Options
Packit 9a2dfb
                   -- ^ Encoding options.
Packit 9a2dfb
                   -> Name
Packit 9a2dfb
                   -- ^ Name of the type for which to generate an instance.
Packit 9a2dfb
                   -> Q [Dec]
Packit 9a2dfb
deriveToJSONCommon = deriveJSONClass [ (ToJSON,     \jc _ -> consToValue Value jc)
Packit 9a2dfb
                                     , (ToEncoding, \jc _ -> consToValue Encoding jc)
Packit 9a2dfb
                                     ]
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a lambda expression which encodes the given data type or
Packit 9a2dfb
-- data family instance constructor as a 'Value'.
Packit 9a2dfb
mkToJSON :: Options -- ^ Encoding options.
Packit 9a2dfb
         -> Name -- ^ Name of the type to encode.
Packit 9a2dfb
         -> Q Exp
Packit 9a2dfb
mkToJSON = mkToJSONCommon toJSONClass
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a lambda expression which encodes the given data type or
Packit 9a2dfb
-- data family instance constructor as a 'Value' by using the given encoding
Packit 9a2dfb
-- function on occurrences of the last type parameter.
Packit 9a2dfb
mkLiftToJSON :: Options -- ^ Encoding options.
Packit 9a2dfb
             -> Name -- ^ Name of the type to encode.
Packit 9a2dfb
             -> Q Exp
Packit 9a2dfb
mkLiftToJSON = mkToJSONCommon toJSON1Class
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a lambda expression which encodes the given data type or
Packit 9a2dfb
-- data family instance constructor as a 'Value' by using the given encoding
Packit 9a2dfb
-- functions on occurrences of the last two type parameters.
Packit 9a2dfb
mkLiftToJSON2 :: Options -- ^ Encoding options.
Packit 9a2dfb
              -> Name -- ^ Name of the type to encode.
Packit 9a2dfb
              -> Q Exp
Packit 9a2dfb
mkLiftToJSON2 = mkToJSONCommon toJSON2Class
Packit 9a2dfb
Packit 9a2dfb
mkToJSONCommon :: JSONClass -- ^ Which class's method is being derived.
Packit 9a2dfb
               -> Options -- ^ Encoding options.
Packit 9a2dfb
               -> Name -- ^ Name of the encoded type.
Packit 9a2dfb
               -> Q Exp
Packit 9a2dfb
mkToJSONCommon = mkFunCommon (\jc _ -> consToValue Value jc)
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a lambda expression which encodes the given data type or
Packit 9a2dfb
-- data family instance constructor as a JSON string.
Packit 9a2dfb
mkToEncoding :: Options -- ^ Encoding options.
Packit 9a2dfb
             -> Name -- ^ Name of the type to encode.
Packit 9a2dfb
             -> Q Exp
Packit 9a2dfb
mkToEncoding = mkToEncodingCommon toJSONClass
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a lambda expression which encodes the given data type or
Packit 9a2dfb
-- data family instance constructor as a JSON string by using the given encoding
Packit 9a2dfb
-- function on occurrences of the last type parameter.
Packit 9a2dfb
mkLiftToEncoding :: Options -- ^ Encoding options.
Packit 9a2dfb
                 -> Name -- ^ Name of the type to encode.
Packit 9a2dfb
                 -> Q Exp
Packit 9a2dfb
mkLiftToEncoding = mkToEncodingCommon toJSON1Class
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a lambda expression which encodes the given data type or
Packit 9a2dfb
-- data family instance constructor as a JSON string by using the given encoding
Packit 9a2dfb
-- functions on occurrences of the last two type parameters.
Packit 9a2dfb
mkLiftToEncoding2 :: Options -- ^ Encoding options.
Packit 9a2dfb
                  -> Name -- ^ Name of the type to encode.
Packit 9a2dfb
                  -> Q Exp
Packit 9a2dfb
mkLiftToEncoding2 = mkToEncodingCommon toJSON2Class
Packit 9a2dfb
Packit 9a2dfb
mkToEncodingCommon :: JSONClass -- ^ Which class's method is being derived.
Packit 9a2dfb
                   -> Options -- ^ Encoding options.
Packit 9a2dfb
                   -> Name -- ^ Name of the encoded type.
Packit 9a2dfb
                   -> Q Exp
Packit 9a2dfb
mkToEncodingCommon = mkFunCommon (\jc _ -> consToValue Encoding jc)
Packit 9a2dfb
Packit 9a2dfb
-- | Helper function used by both 'deriveToJSON' and 'mkToJSON'. Generates
Packit 9a2dfb
-- code to generate a 'Value' or 'Encoding' of a number of constructors. All
Packit 9a2dfb
-- constructors must be from the same type.
Packit 9a2dfb
consToValue :: ToJSONFun
Packit 9a2dfb
            -- ^ The method ('toJSON' or 'toEncoding') being derived.
Packit 9a2dfb
            -> JSONClass
Packit 9a2dfb
            -- ^ The ToJSON variant being derived.
Packit 9a2dfb
            -> Options
Packit 9a2dfb
            -- ^ Encoding options.
Packit 9a2dfb
            -> [Type]
Packit 9a2dfb
            -- ^ The types from the data type/data family instance declaration
Packit 9a2dfb
            -> [ConstructorInfo]
Packit 9a2dfb
            -- ^ Constructors for which to generate JSON generating code.
Packit 9a2dfb
            -> Q Exp
Packit 9a2dfb
Packit 9a2dfb
consToValue _ _ _ _ [] = error $ "Data.Aeson.TH.consToValue: "
Packit 9a2dfb
                             ++ "Not a single constructor given!"
Packit 9a2dfb
Packit 9a2dfb
consToValue target jc opts vars cons = do
Packit 9a2dfb
    value <- newName "value"
Packit 9a2dfb
    tjs   <- newNameList "_tj"  $ arityInt jc
Packit 9a2dfb
    tjls  <- newNameList "_tjl" $ arityInt jc
Packit 9a2dfb
    let zippedTJs      = zip tjs tjls
Packit 9a2dfb
        interleavedTJs = interleave tjs tjls
Packit 9a2dfb
        lastTyVars     = map varTToName $ drop (length vars - arityInt jc) vars
Packit 9a2dfb
        tvMap          = M.fromList $ zip lastTyVars zippedTJs
Packit 9a2dfb
    lamE (map varP $ interleavedTJs ++ [value]) $
Packit 9a2dfb
        caseE (varE value) (matches tvMap)
Packit 9a2dfb
  where
Packit 9a2dfb
    matches tvMap = case cons of
Packit 9a2dfb
      -- A single constructor is directly encoded. The constructor itself may be
Packit 9a2dfb
      -- forgotten.
Packit 9a2dfb
      [con] | not (tagSingleConstructors opts) -> [argsToValue target jc tvMap opts False con]
Packit 9a2dfb
      _ | allNullaryToStringTag opts && all isNullary cons ->
Packit 9a2dfb
              [ match (conP conName []) (normalB $ conStr target opts conName) []
Packit 9a2dfb
              | con <- cons
Packit 9a2dfb
              , let conName = constructorName con
Packit 9a2dfb
              ]
Packit 9a2dfb
        | otherwise -> [argsToValue target jc tvMap opts True con | con <- cons]
Packit 9a2dfb
Packit 9a2dfb
-- | Name of the constructor as a quoted 'Value' or 'Encoding'.
Packit 9a2dfb
conStr :: ToJSONFun -> Options -> Name -> Q Exp
Packit 9a2dfb
conStr Value opts = appE [|String|] . conTxt opts
Packit 9a2dfb
conStr Encoding opts = appE [|E.text|] . conTxt opts
Packit 9a2dfb
Packit 9a2dfb
-- | Name of the constructor as a quoted 'Text'.
Packit 9a2dfb
conTxt :: Options -> Name -> Q Exp
Packit 9a2dfb
conTxt opts = appE [|T.pack|] . stringE . conString opts
Packit 9a2dfb
Packit 9a2dfb
-- | Name of the constructor.
Packit 9a2dfb
conString :: Options -> Name -> String
Packit 9a2dfb
conString opts = constructorTagModifier opts . nameBase
Packit 9a2dfb
Packit 9a2dfb
-- | If constructor is nullary.
Packit 9a2dfb
isNullary :: ConstructorInfo -> Bool
Packit 9a2dfb
isNullary ConstructorInfo { constructorVariant = NormalConstructor
Packit 9a2dfb
                          , constructorFields  = tys } = null tys
Packit 9a2dfb
isNullary _ = False
Packit 9a2dfb
Packit 9a2dfb
-- | Wrap fields of a non-record constructor. See 'sumToValue'.
Packit 9a2dfb
opaqueSumToValue :: ToJSONFun -> Options -> Bool -> Bool -> Name -> ExpQ -> ExpQ
Packit 9a2dfb
opaqueSumToValue target opts multiCons nullary conName value =
Packit 9a2dfb
  sumToValue target opts multiCons nullary conName
Packit 9a2dfb
    value
Packit 9a2dfb
    pairs
Packit 9a2dfb
  where
Packit 9a2dfb
    pairs contentsFieldName = listE [toPair target contentsFieldName value]
Packit 9a2dfb
Packit 9a2dfb
-- | Wrap fields of a record constructor. See 'sumToValue'.
Packit 9a2dfb
recordSumToValue :: ToJSONFun -> Options -> Bool -> Bool -> Name -> ExpQ -> ExpQ
Packit 9a2dfb
recordSumToValue target opts multiCons nullary conName pairs =
Packit 9a2dfb
  sumToValue target opts multiCons nullary conName
Packit 9a2dfb
    (objectExp target pairs)
Packit 9a2dfb
    (const pairs)
Packit 9a2dfb
Packit 9a2dfb
-- | Wrap fields of a constructor.
Packit 9a2dfb
sumToValue
Packit 9a2dfb
  :: ToJSONFun
Packit 9a2dfb
  -- ^ The method being derived.
Packit 9a2dfb
  -> Options
Packit 9a2dfb
  -- ^ Deriving options.
Packit 9a2dfb
  -> Bool
Packit 9a2dfb
  -- ^ Does this type have multiple constructors.
Packit 9a2dfb
  -> Bool
Packit 9a2dfb
  -- ^ Is this constructor nullary.
Packit 9a2dfb
  -> Name
Packit 9a2dfb
  -- ^ Constructor name.
Packit 9a2dfb
  -> ExpQ
Packit 9a2dfb
  -- ^ Fields of the constructor as a 'Value' or 'Encoding'.
Packit 9a2dfb
  -> (String -> ExpQ)
Packit 9a2dfb
  -- ^ Representation of an 'Object' fragment used for the 'TaggedObject'
Packit 9a2dfb
  -- variant; of type @[(Text,Value)]@ or @[Encoding]@, depending on the method
Packit 9a2dfb
  -- being derived.
Packit 9a2dfb
  --
Packit 9a2dfb
  -- - For non-records, produces a pair @"contentsFieldName":value@,
Packit 9a2dfb
  --   given a @contentsFieldName@ as an argument. See 'opaqueSumToValue'.
Packit 9a2dfb
  -- - For records, produces the list of pairs corresponding to fields of the
Packit 9a2dfb
  --   encoded value (ignores the argument). See 'recordSumToValue'.
Packit 9a2dfb
  -> ExpQ
Packit 9a2dfb
sumToValue target opts multiCons nullary conName value pairs
Packit 9a2dfb
    | multiCons =
Packit 9a2dfb
        case sumEncoding opts of
Packit 9a2dfb
          TwoElemArray ->
Packit 9a2dfb
            array target [conStr target opts conName, value]
Packit 9a2dfb
          TaggedObject{tagFieldName, contentsFieldName} ->
Packit 9a2dfb
            -- TODO: Maybe throw an error in case
Packit 9a2dfb
            -- tagFieldName overwrites a field in pairs.
Packit 9a2dfb
            let tag = toPair target tagFieldName (conStr target opts conName)
Packit 9a2dfb
                content = pairs contentsFieldName
Packit 9a2dfb
            in objectExp target $
Packit 9a2dfb
              if nullary then listE [tag] else infixApp tag [|(:)|] content
Packit 9a2dfb
          ObjectWithSingleField ->
Packit 9a2dfb
            object target [(conString opts conName, value)]
Packit 9a2dfb
          UntaggedValue | nullary -> conStr target opts conName
Packit 9a2dfb
          UntaggedValue -> value
Packit 9a2dfb
    | otherwise = value
Packit 9a2dfb
Packit 9a2dfb
-- | Generates code to generate the JSON encoding of a single constructor.
Packit 9a2dfb
argsToValue :: ToJSONFun -> JSONClass -> TyVarMap -> Options -> Bool -> ConstructorInfo -> Q Match
Packit 9a2dfb
Packit 9a2dfb
-- Polyadic constructors with special case for unary constructors.
Packit 9a2dfb
argsToValue target jc tvMap opts multiCons
Packit 9a2dfb
  ConstructorInfo { constructorName    = conName
Packit 9a2dfb
                  , constructorVariant = NormalConstructor
Packit 9a2dfb
                  , constructorFields  = argTys } = do
Packit 9a2dfb
    argTys' <- mapM resolveTypeSynonyms argTys
Packit 9a2dfb
    let len = length argTys'
Packit 9a2dfb
    args <- newNameList "arg" len
Packit 9a2dfb
    let js = case [ dispatchToJSON target jc conName tvMap argTy
Packit 9a2dfb
                      `appE` varE arg
Packit 9a2dfb
                  | (arg, argTy) <- zip args argTys'
Packit 9a2dfb
                  ] of
Packit 9a2dfb
               -- Single argument is directly converted.
Packit 9a2dfb
               [e] -> e
Packit 9a2dfb
               -- Zero and multiple arguments are converted to a JSON array.
Packit 9a2dfb
               es -> array target es
Packit 9a2dfb
Packit 9a2dfb
    match (conP conName $ map varP args)
Packit 9a2dfb
          (normalB $ opaqueSumToValue target opts multiCons (null argTys') conName js)
Packit 9a2dfb
          []
Packit 9a2dfb
Packit 9a2dfb
-- Records.
Packit 9a2dfb
argsToValue target jc tvMap opts multiCons
Packit 9a2dfb
  info@ConstructorInfo { constructorName    = conName
Packit 9a2dfb
                       , constructorVariant = RecordConstructor fields
Packit 9a2dfb
                       , constructorFields  = argTys } =
Packit 9a2dfb
    case (unwrapUnaryRecords opts, not multiCons, argTys) of
Packit 9a2dfb
      (True,True,[_]) -> argsToValue target jc tvMap opts multiCons
Packit 9a2dfb
                                     (info{constructorVariant = NormalConstructor})
Packit 9a2dfb
      _ -> do
Packit 9a2dfb
        argTys' <- mapM resolveTypeSynonyms argTys
Packit 9a2dfb
        args <- newNameList "arg" $ length argTys'
Packit 9a2dfb
        let pairs | omitNothingFields opts = infixApp maybeFields
Packit 9a2dfb
                                                      [|(++)|]
Packit 9a2dfb
                                                      restFields
Packit 9a2dfb
                  | otherwise = listE $ map pureToPair argCons
Packit 9a2dfb
Packit 9a2dfb
            argCons = zip3 (map varE args) argTys' fields
Packit 9a2dfb
Packit 9a2dfb
            maybeFields = [|catMaybes|] `appE` listE (map maybeToPair maybes)
Packit 9a2dfb
Packit 9a2dfb
            restFields = listE $ map pureToPair rest
Packit 9a2dfb
Packit 9a2dfb
            (maybes0, rest0) = partition isMaybe argCons
Packit 9a2dfb
            (options, rest) = partition isOption rest0
Packit 9a2dfb
            maybes = maybes0 ++ map optionToMaybe options
Packit 9a2dfb
Packit 9a2dfb
            maybeToPair = toPairLifted True
Packit 9a2dfb
            pureToPair = toPairLifted False
Packit 9a2dfb
Packit 9a2dfb
            toPairLifted lifted (arg, argTy, field) =
Packit 9a2dfb
              let toValue = dispatchToJSON target jc conName tvMap argTy
Packit 9a2dfb
                  fieldName = fieldLabel opts field
Packit 9a2dfb
                  e arg' = toPair target fieldName (toValue `appE` arg')
Packit 9a2dfb
              in if lifted
Packit 9a2dfb
                then do
Packit 9a2dfb
                  x <- newName "x"
Packit 9a2dfb
                  infixApp (lam1E (varP x) (e (varE x))) [|(<$>)|] arg
Packit 9a2dfb
                else e arg
Packit 9a2dfb
Packit 9a2dfb
        match (conP conName $ map varP args)
Packit 9a2dfb
              (normalB $ recordSumToValue target opts multiCons (null argTys) conName pairs)
Packit 9a2dfb
              []
Packit 9a2dfb
Packit 9a2dfb
-- Infix constructors.
Packit 9a2dfb
argsToValue target jc tvMap opts multiCons
Packit 9a2dfb
  ConstructorInfo { constructorName    = conName
Packit 9a2dfb
                  , constructorVariant = InfixConstructor
Packit 9a2dfb
                  , constructorFields  = argTys } = do
Packit 9a2dfb
    [alTy, arTy] <- mapM resolveTypeSynonyms argTys
Packit 9a2dfb
    al <- newName "argL"
Packit 9a2dfb
    ar <- newName "argR"
Packit 9a2dfb
    match (infixP (varP al) conName (varP ar))
Packit 9a2dfb
          ( normalB
Packit 9a2dfb
          $ opaqueSumToValue target opts multiCons False conName
Packit 9a2dfb
          $ array target
Packit 9a2dfb
              [ dispatchToJSON target jc conName tvMap aTy
Packit 9a2dfb
                  `appE` varE a
Packit 9a2dfb
              | (a, aTy) <- [(al,alTy), (ar,arTy)]
Packit 9a2dfb
              ]
Packit 9a2dfb
          )
Packit 9a2dfb
          []
Packit 9a2dfb
Packit 9a2dfb
isMaybe :: (a, Type, b) -> Bool
Packit 9a2dfb
isMaybe (_, AppT (ConT t) _, _) = t == ''Maybe
Packit 9a2dfb
isMaybe _                       = False
Packit 9a2dfb
Packit 9a2dfb
isOption :: (a, Type, b) -> Bool
Packit 9a2dfb
isOption (_, AppT (ConT t) _, _) = t == ''Semigroup.Option
Packit 9a2dfb
isOption _                       = False
Packit 9a2dfb
Packit 9a2dfb
optionToMaybe :: (ExpQ, b, c) -> (ExpQ, b, c)
Packit 9a2dfb
optionToMaybe (a, b, c) = ([|Semigroup.getOption|] `appE` a, b, c)
Packit 9a2dfb
Packit 9a2dfb
(<^>) :: ExpQ -> ExpQ -> ExpQ
Packit 9a2dfb
(<^>) a b = infixApp a [|(E.><)|] b
Packit 9a2dfb
infixr 6 <^>
Packit 9a2dfb
Packit 9a2dfb
(<:>) :: ExpQ -> ExpQ -> ExpQ
Packit 9a2dfb
(<:>) a b = a <^> [|E.colon|] <^> b
Packit 9a2dfb
infixr 5 <:>
Packit 9a2dfb
Packit 9a2dfb
(<%>) :: ExpQ -> ExpQ -> ExpQ
Packit 9a2dfb
(<%>) a b = a <^> [|E.comma|] <^> b
Packit 9a2dfb
infixr 4 <%>
Packit 9a2dfb
Packit 9a2dfb
-- | Wrap a list of quoted 'Value's in a quoted 'Array' (of type 'Value').
Packit 9a2dfb
array :: ToJSONFun -> [ExpQ] -> ExpQ
Packit 9a2dfb
array Encoding [] = [|E.emptyArray_|]
Packit 9a2dfb
array Value [] = [|Array V.empty|]
Packit 9a2dfb
array Encoding es = [|E.wrapArray|] `appE` foldr1 (<%>) es
Packit 9a2dfb
array Value es = do
Packit 9a2dfb
  mv <- newName "mv"
Packit 9a2dfb
  let newMV = bindS (varP mv)
Packit 9a2dfb
                    ([|VM.unsafeNew|] `appE`
Packit 9a2dfb
                      litE (integerL $ fromIntegral (length es)))
Packit 9a2dfb
      stmts = [ noBindS $
Packit 9a2dfb
                  [|VM.unsafeWrite|] `appE`
Packit 9a2dfb
                    varE mv `appE`
Packit 9a2dfb
                      litE (integerL ix) `appE`
Packit 9a2dfb
                        e
Packit 9a2dfb
              | (ix, e) <- zip [(0::Integer)..] es
Packit 9a2dfb
              ]
Packit 9a2dfb
      ret = noBindS $ [|return|] `appE` varE mv
Packit 9a2dfb
  [|Array|] `appE`
Packit 9a2dfb
             (varE 'V.create `appE`
Packit 9a2dfb
               doE (newMV:stmts++[ret]))
Packit 9a2dfb
Packit 9a2dfb
-- | Wrap an associative list of keys and quoted values in a quoted 'Object'.
Packit 9a2dfb
object :: ToJSONFun -> [(String, ExpQ)] -> ExpQ
Packit 9a2dfb
object target = wrapObject target . catPairs target . fmap (uncurry (toPair target))
Packit 9a2dfb
Packit 9a2dfb
-- |
Packit 9a2dfb
-- - When deriving 'ToJSON', map a list of quoted key-value pairs to an
Packit 9a2dfb
--   expression of the list of pairs.
Packit 9a2dfb
-- - When deriving 'ToEncoding', map a list of quoted 'Encoding's representing
Packit 9a2dfb
--   key-value pairs to a comma-separated 'Encoding' of them.
Packit 9a2dfb
--
Packit 9a2dfb
-- > catPairs Value [ [|(k0,v0)|], [|(k1,v1)|] ] = [| [(k0,v0), (k1,v1)] |]
Packit 9a2dfb
-- > catPairs Encoding [ [|"\"k0\":v0"|], [|"\"k1\":v1"|] ] = [| "\"k0\":v0,\"k1\":v1" |]
Packit 9a2dfb
catPairs :: ToJSONFun -> [ExpQ] -> ExpQ
Packit 9a2dfb
catPairs Value = listE
Packit 9a2dfb
catPairs Encoding = foldr1 (<%>)
Packit 9a2dfb
Packit 9a2dfb
-- |
Packit 9a2dfb
-- - When deriving 'ToJSON', wrap a quoted list of key-value pairs in an 'Object'.
Packit 9a2dfb
-- - When deriving 'ToEncoding', wrap a quoted list of encoded key-value pairs
Packit 9a2dfb
--   in an encoded 'Object'.
Packit 9a2dfb
--
Packit 9a2dfb
-- > objectExp Value [| [(k0,v0), (k1,v1)] |] = [| Object (fromList [(k0,v0), (k1,v1)]) |]
Packit 9a2dfb
-- > objectExp Encoding [| ["\"k0\":v0", "\"k1\":v1"] |] = [| "{\"k0\":v0,\"k1\":v1}" |]
Packit 9a2dfb
objectExp :: ToJSONFun -> ExpQ -> ExpQ
Packit 9a2dfb
objectExp target = wrapObject target . catPairsExp target
Packit 9a2dfb
Packit 9a2dfb
-- | Counterpart of 'catPairsExp' when the list of pairs is already quoted.
Packit 9a2dfb
--
Packit 9a2dfb
-- > objectExp Value [| [(k0,v0), (k1,v1)] |] = [| [(k0,v0), (k1,v1)] |]
Packit 9a2dfb
-- > objectExp Encoding [| ["\"k0\":v0", "\"k1\":v1"] |] = [| "\"k0\":v0,\"k1\":v1" |]
Packit 9a2dfb
catPairsExp :: ToJSONFun -> ExpQ -> ExpQ
Packit 9a2dfb
catPairsExp Value e = e
Packit 9a2dfb
catPairsExp Encoding e = [|commaSep|] `appE` e
Packit 9a2dfb
Packit 9a2dfb
-- | Create (an encoding of) a key-value pair.
Packit 9a2dfb
--
Packit 9a2dfb
-- > toPair Value "k" [|v|] = [|("k",v)|]  -- The quoted string is actually Text.
Packit 9a2dfb
-- > toPair Encoding "k" [|"v"|] = [|"\"k\":v"|]
Packit 9a2dfb
toPair :: ToJSONFun -> String -> ExpQ -> ExpQ
Packit 9a2dfb
toPair Value k v = infixApp [|T.pack k|] [|(.=)|] v
Packit 9a2dfb
toPair Encoding k v = [|E.string k|] <:> v
Packit 9a2dfb
Packit 9a2dfb
-- | Map an associative list in an 'Object'.
Packit 9a2dfb
--
Packit 9a2dfb
-- > wrapObject Value [| [(k0,v0), (k1,v1)] |] = [| Object (fromList [(k0,v0), (k1,v1)]) |]
Packit 9a2dfb
-- > wrapObject Encoding [| "\"k0\":v0,\"k1\":v1" |] = [| "{\"k0\":v0,\"k1\":v1}" |]
Packit 9a2dfb
wrapObject :: ToJSONFun -> ExpQ -> ExpQ
Packit 9a2dfb
wrapObject Value e = [|A.object|] `appE` e
Packit 9a2dfb
wrapObject Encoding e = [|E.wrapObject|] `appE` e
Packit 9a2dfb
Packit 9a2dfb
-- | Separate 'Encoding's by commas.
Packit 9a2dfb
--
Packit 9a2dfb
-- > commaSep ["a","b","c"] = "a,b,c"
Packit 9a2dfb
commaSep :: [E.Encoding] -> E.Encoding
Packit 9a2dfb
commaSep [] = E.empty
Packit 9a2dfb
commaSep [x] = x
Packit 9a2dfb
commaSep (x : xs) = x E.>< E.comma E.>< commaSep xs
Packit 9a2dfb
Packit 9a2dfb
--------------------------------------------------------------------------------
Packit 9a2dfb
-- FromJSON
Packit 9a2dfb
--------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a 'FromJSON' instance declaration for the given data type or
Packit 9a2dfb
-- data family instance constructor.
Packit 9a2dfb
deriveFromJSON :: Options
Packit 9a2dfb
               -- ^ Encoding options.
Packit 9a2dfb
               -> Name
Packit 9a2dfb
               -- ^ Name of the type for which to generate a 'FromJSON' instance
Packit 9a2dfb
               -- declaration.
Packit 9a2dfb
               -> Q [Dec]
Packit 9a2dfb
deriveFromJSON = deriveFromJSONCommon fromJSONClass
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a 'FromJSON1' instance declaration for the given data type or
Packit 9a2dfb
-- data family instance constructor.
Packit 9a2dfb
deriveFromJSON1 :: Options
Packit 9a2dfb
                -- ^ Encoding options.
Packit 9a2dfb
                -> Name
Packit 9a2dfb
                -- ^ Name of the type for which to generate a 'FromJSON1' instance
Packit 9a2dfb
                -- declaration.
Packit 9a2dfb
                -> Q [Dec]
Packit 9a2dfb
deriveFromJSON1 = deriveFromJSONCommon fromJSON1Class
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a 'FromJSON2' instance declaration for the given data type or
Packit 9a2dfb
-- data family instance constructor.
Packit 9a2dfb
deriveFromJSON2 :: Options
Packit 9a2dfb
                -- ^ Encoding options.
Packit 9a2dfb
                -> Name
Packit 9a2dfb
                -- ^ Name of the type for which to generate a 'FromJSON3' instance
Packit 9a2dfb
                -- declaration.
Packit 9a2dfb
                -> Q [Dec]
Packit 9a2dfb
deriveFromJSON2 = deriveFromJSONCommon fromJSON2Class
Packit 9a2dfb
Packit 9a2dfb
deriveFromJSONCommon :: JSONClass
Packit 9a2dfb
                     -- ^ The FromJSON variant being derived.
Packit 9a2dfb
                     -> Options
Packit 9a2dfb
                     -- ^ Encoding options.
Packit 9a2dfb
                     -> Name
Packit 9a2dfb
                     -- ^ Name of the type for which to generate an instance.
Packit 9a2dfb
                     -- declaration.
Packit 9a2dfb
                     -> Q [Dec]
Packit 9a2dfb
deriveFromJSONCommon = deriveJSONClass [(ParseJSON, consFromJSON)]
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a lambda expression which parses the JSON encoding of the given
Packit 9a2dfb
-- data type or data family instance constructor.
Packit 9a2dfb
mkParseJSON :: Options -- ^ Encoding options.
Packit 9a2dfb
            -> Name -- ^ Name of the encoded type.
Packit 9a2dfb
            -> Q Exp
Packit 9a2dfb
mkParseJSON = mkParseJSONCommon fromJSONClass
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a lambda expression which parses the JSON encoding of the given
Packit 9a2dfb
-- data type or data family instance constructor by using the given parsing
Packit 9a2dfb
-- function on occurrences of the last type parameter.
Packit 9a2dfb
mkLiftParseJSON :: Options -- ^ Encoding options.
Packit 9a2dfb
                -> Name -- ^ Name of the encoded type.
Packit 9a2dfb
                -> Q Exp
Packit 9a2dfb
mkLiftParseJSON = mkParseJSONCommon fromJSON1Class
Packit 9a2dfb
Packit 9a2dfb
-- | Generates a lambda expression which parses the JSON encoding of the given
Packit 9a2dfb
-- data type or data family instance constructor by using the given parsing
Packit 9a2dfb
-- functions on occurrences of the last two type parameters.
Packit 9a2dfb
mkLiftParseJSON2 :: Options -- ^ Encoding options.
Packit 9a2dfb
                 -> Name -- ^ Name of the encoded type.
Packit 9a2dfb
                 -> Q Exp
Packit 9a2dfb
mkLiftParseJSON2 = mkParseJSONCommon fromJSON2Class
Packit 9a2dfb
Packit 9a2dfb
mkParseJSONCommon :: JSONClass -- ^ Which class's method is being derived.
Packit 9a2dfb
                  -> Options -- ^ Encoding options.
Packit 9a2dfb
                  -> Name -- ^ Name of the encoded type.
Packit 9a2dfb
                  -> Q Exp
Packit 9a2dfb
mkParseJSONCommon = mkFunCommon consFromJSON
Packit 9a2dfb
Packit 9a2dfb
-- | Helper function used by both 'deriveFromJSON' and 'mkParseJSON'. Generates
Packit 9a2dfb
-- code to parse the JSON encoding of a number of constructors. All constructors
Packit 9a2dfb
-- must be from the same type.
Packit 9a2dfb
consFromJSON :: JSONClass
Packit 9a2dfb
             -- ^ The FromJSON variant being derived.
Packit 9a2dfb
             -> Name
Packit 9a2dfb
             -- ^ Name of the type to which the constructors belong.
Packit 9a2dfb
             -> Options
Packit 9a2dfb
             -- ^ Encoding options
Packit 9a2dfb
             -> [Type]
Packit 9a2dfb
             -- ^ The types from the data type/data family instance declaration
Packit 9a2dfb
             -> [ConstructorInfo]
Packit 9a2dfb
             -- ^ Constructors for which to generate JSON parsing code.
Packit 9a2dfb
             -> Q Exp
Packit 9a2dfb
Packit 9a2dfb
consFromJSON _ _ _ _ [] = error $ "Data.Aeson.TH.consFromJSON: "
Packit 9a2dfb
                                ++ "Not a single constructor given!"
Packit 9a2dfb
Packit 9a2dfb
consFromJSON jc tName opts vars cons = do
Packit 9a2dfb
  value <- newName "value"
Packit 9a2dfb
  pjs   <- newNameList "_pj"  $ arityInt jc
Packit 9a2dfb
  pjls  <- newNameList "_pjl" $ arityInt jc
Packit 9a2dfb
  let zippedPJs      = zip pjs pjls
Packit 9a2dfb
      interleavedPJs = interleave pjs pjls
Packit 9a2dfb
      lastTyVars     = map varTToName $ drop (length vars - arityInt jc) vars
Packit 9a2dfb
      tvMap          = M.fromList $ zip lastTyVars zippedPJs
Packit 9a2dfb
  lamE (map varP $ interleavedPJs ++ [value]) $ lamExpr value tvMap
Packit 9a2dfb
Packit 9a2dfb
  where
Packit 9a2dfb
    checkExi tvMap con = checkExistentialContext jc tvMap
Packit 9a2dfb
                                                 (constructorContext con)
Packit 9a2dfb
                                                 (constructorName con)
Packit 9a2dfb
Packit 9a2dfb
    lamExpr value tvMap = case cons of
Packit 9a2dfb
      [con]
Packit 9a2dfb
        | not (tagSingleConstructors opts)
Packit 9a2dfb
            -> checkExi tvMap con $ parseArgs jc tvMap tName opts con (Right value)
Packit 9a2dfb
      _ | sumEncoding opts == UntaggedValue
Packit 9a2dfb
            -> parseUntaggedValue tvMap cons value
Packit 9a2dfb
        | otherwise
Packit 9a2dfb
            -> caseE (varE value) $
Packit 9a2dfb
                   if allNullaryToStringTag opts && all isNullary cons
Packit 9a2dfb
                   then allNullaryMatches
Packit 9a2dfb
                   else mixedMatches tvMap
Packit 9a2dfb
Packit 9a2dfb
    allNullaryMatches =
Packit 9a2dfb
      [ do txt <- newName "txt"
Packit 9a2dfb
           match (conP 'String [varP txt])
Packit 9a2dfb
                 (guardedB $
Packit 9a2dfb
                  [ liftM2 (,) (normalG $
Packit 9a2dfb
                                  infixApp (varE txt)
Packit 9a2dfb
                                           [|(==)|]
Packit 9a2dfb
                                           (conTxt opts conName)
Packit 9a2dfb
                               )
Packit 9a2dfb
                               ([|pure|] `appE` conE conName)
Packit 9a2dfb
                  | con <- cons
Packit 9a2dfb
                  , let conName = constructorName con
Packit 9a2dfb
                  ]
Packit 9a2dfb
                  ++
Packit 9a2dfb
                  [ liftM2 (,)
Packit 9a2dfb
                      (normalG [|otherwise|])
Packit 9a2dfb
                      ( [|noMatchFail|]
Packit 9a2dfb
                        `appE` litE (stringL $ show tName)
Packit 9a2dfb
                        `appE` ([|T.unpack|] `appE` varE txt)
Packit 9a2dfb
                      )
Packit 9a2dfb
                  ]
Packit 9a2dfb
                 )
Packit 9a2dfb
                 []
Packit 9a2dfb
      , do other <- newName "other"
Packit 9a2dfb
           match (varP other)
Packit 9a2dfb
                 (normalB $ [|noStringFail|]
Packit 9a2dfb
                    `appE` litE (stringL $ show tName)
Packit 9a2dfb
                    `appE` ([|valueConName|] `appE` varE other)
Packit 9a2dfb
                 )
Packit 9a2dfb
                 []
Packit 9a2dfb
      ]
Packit 9a2dfb
Packit 9a2dfb
    mixedMatches tvMap =
Packit 9a2dfb
        case sumEncoding opts of
Packit 9a2dfb
          TaggedObject {tagFieldName, contentsFieldName} ->
Packit 9a2dfb
            parseObject $ parseTaggedObject tvMap tagFieldName contentsFieldName
Packit 9a2dfb
          UntaggedValue -> error "UntaggedValue: Should be handled already"
Packit 9a2dfb
          ObjectWithSingleField ->
Packit 9a2dfb
            parseObject $ parseObjectWithSingleField tvMap
Packit 9a2dfb
          TwoElemArray ->
Packit 9a2dfb
            [ do arr <- newName "array"
Packit 9a2dfb
                 match (conP 'Array [varP arr])
Packit 9a2dfb
                       (guardedB
Packit 9a2dfb
                        [ liftM2 (,) (normalG $ infixApp ([|V.length|] `appE` varE arr)
Packit 9a2dfb
                                                         [|(==)|]
Packit 9a2dfb
                                                         (litE $ integerL 2))
Packit 9a2dfb
                                     (parse2ElemArray tvMap arr)
Packit 9a2dfb
                        , liftM2 (,) (normalG [|otherwise|])
Packit 9a2dfb
                                     ([|not2ElemArray|]
Packit 9a2dfb
                                       `appE` litE (stringL $ show tName)
Packit 9a2dfb
                                       `appE` ([|V.length|] `appE` varE arr))
Packit 9a2dfb
                        ]
Packit 9a2dfb
                       )
Packit 9a2dfb
                       []
Packit 9a2dfb
            , do other <- newName "other"
Packit 9a2dfb
                 match (varP other)
Packit 9a2dfb
                       ( normalB
Packit 9a2dfb
                         $ [|noArrayFail|]
Packit 9a2dfb
                             `appE` litE (stringL $ show tName)
Packit 9a2dfb
                             `appE` ([|valueConName|] `appE` varE other)
Packit 9a2dfb
                       )
Packit 9a2dfb
                       []
Packit 9a2dfb
            ]
Packit 9a2dfb
Packit 9a2dfb
    parseObject f =
Packit 9a2dfb
        [ do obj <- newName "obj"
Packit 9a2dfb
             match (conP 'Object [varP obj]) (normalB $ f obj) []
Packit 9a2dfb
        , do other <- newName "other"
Packit 9a2dfb
             match (varP other)
Packit 9a2dfb
                   ( normalB
Packit 9a2dfb
                     $ [|noObjectFail|]
Packit 9a2dfb
                         `appE` litE (stringL $ show tName)
Packit 9a2dfb
                         `appE` ([|valueConName|] `appE` varE other)
Packit 9a2dfb
                   )
Packit 9a2dfb
                   []
Packit 9a2dfb
        ]
Packit 9a2dfb
Packit 9a2dfb
    parseTaggedObject tvMap typFieldName valFieldName obj = do
Packit 9a2dfb
      conKey <- newName "conKey"
Packit 9a2dfb
      doE [ bindS (varP conKey)
Packit 9a2dfb
                  (infixApp (varE obj)
Packit 9a2dfb
                            [|(.:)|]
Packit 9a2dfb
                            ([|T.pack|] `appE` stringE typFieldName))
Packit 9a2dfb
          , noBindS $ parseContents tvMap conKey (Left (valFieldName, obj)) 'conNotFoundFailTaggedObject
Packit 9a2dfb
          ]
Packit 9a2dfb
Packit 9a2dfb
    parseUntaggedValue tvMap cons' conVal =
Packit 9a2dfb
        foldr1 (\e e' -> infixApp e [|(<|>)|] e')
Packit 9a2dfb
               (map (\x -> parseValue tvMap x conVal) cons')
Packit 9a2dfb
Packit 9a2dfb
    parseValue _tvMap
Packit 9a2dfb
        ConstructorInfo { constructorName    = conName
Packit 9a2dfb
                        , constructorVariant = NormalConstructor
Packit 9a2dfb
                        , constructorFields  = [] }
Packit 9a2dfb
        conVal = do
Packit 9a2dfb
      str <- newName "str"
Packit 9a2dfb
      caseE (varE conVal)
Packit 9a2dfb
        [ match (conP 'String [varP str])
Packit 9a2dfb
                (guardedB
Packit 9a2dfb
                  [ liftM2 (,) (normalG $ infixApp (varE str) [|(==)|] (conTxt opts conName)
Packit 9a2dfb
                               )
Packit 9a2dfb
                               ([|pure|] `appE` conE conName)
Packit 9a2dfb
                  ]
Packit 9a2dfb
                )
Packit 9a2dfb
                []
Packit 9a2dfb
        , matchFailed tName conName "String"
Packit 9a2dfb
        ]
Packit 9a2dfb
    parseValue tvMap con conVal =
Packit 9a2dfb
      checkExi tvMap con $ parseArgs jc tvMap tName opts con (Right conVal)
Packit 9a2dfb
Packit 9a2dfb
Packit 9a2dfb
    parse2ElemArray tvMap arr = do
Packit 9a2dfb
      conKey <- newName "conKey"
Packit 9a2dfb
      conVal <- newName "conVal"
Packit 9a2dfb
      let letIx n ix =
Packit 9a2dfb
              valD (varP n)
Packit 9a2dfb
                   (normalB ([|V.unsafeIndex|] `appE`
Packit 9a2dfb
                               varE arr `appE`
Packit 9a2dfb
                               litE (integerL ix)))
Packit 9a2dfb
                   []
Packit 9a2dfb
      letE [ letIx conKey 0
Packit 9a2dfb
           , letIx conVal 1
Packit 9a2dfb
           ]
Packit 9a2dfb
           (caseE (varE conKey)
Packit 9a2dfb
                  [ do txt <- newName "txt"
Packit 9a2dfb
                       match (conP 'String [varP txt])
Packit 9a2dfb
                             (normalB $ parseContents tvMap
Packit 9a2dfb
                                                      txt
Packit 9a2dfb
                                                      (Right conVal)
Packit 9a2dfb
                                                      'conNotFoundFail2ElemArray
Packit 9a2dfb
                             )
Packit 9a2dfb
                             []
Packit 9a2dfb
                  , do other <- newName "other"
Packit 9a2dfb
                       match (varP other)
Packit 9a2dfb
                             ( normalB
Packit 9a2dfb
                               $ [|firstElemNoStringFail|]
Packit 9a2dfb
                                     `appE` litE (stringL $ show tName)
Packit 9a2dfb
                                     `appE` ([|valueConName|] `appE` varE other)
Packit 9a2dfb
                             )
Packit 9a2dfb
                             []
Packit 9a2dfb
                  ]
Packit 9a2dfb
           )
Packit 9a2dfb
Packit 9a2dfb
    parseObjectWithSingleField tvMap obj = do
Packit 9a2dfb
      conKey <- newName "conKey"
Packit 9a2dfb
      conVal <- newName "conVal"
Packit 9a2dfb
      caseE ([e|H.toList|] `appE` varE obj)
Packit 9a2dfb
            [ match (listP [tupP [varP conKey, varP conVal]])
Packit 9a2dfb
                    (normalB $ parseContents tvMap conKey (Right conVal) 'conNotFoundFailObjectSingleField)
Packit 9a2dfb
                    []
Packit 9a2dfb
            , do other <- newName "other"
Packit 9a2dfb
                 match (varP other)
Packit 9a2dfb
                       (normalB $ [|wrongPairCountFail|]
Packit 9a2dfb
                                  `appE` litE (stringL $ show tName)
Packit 9a2dfb
                                  `appE` ([|show . length|] `appE` varE other)
Packit 9a2dfb
                       )
Packit 9a2dfb
                       []
Packit 9a2dfb
            ]
Packit 9a2dfb
Packit 9a2dfb
    parseContents tvMap conKey contents errorFun =
Packit 9a2dfb
        caseE (varE conKey)
Packit 9a2dfb
              [ match wildP
Packit 9a2dfb
                      ( guardedB $
Packit 9a2dfb
                        [ do g <- normalG $ infixApp (varE conKey)
Packit 9a2dfb
                                                     [|(==)|]
Packit 9a2dfb
                                                     ([|T.pack|] `appE`
Packit 9a2dfb
                                                        conNameExp opts con)
Packit 9a2dfb
                             e <- checkExi tvMap con $
Packit 9a2dfb
                                  parseArgs jc tvMap tName opts con contents
Packit 9a2dfb
                             return (g, e)
Packit 9a2dfb
                        | con <- cons
Packit 9a2dfb
                        ]
Packit 9a2dfb
                        ++
Packit 9a2dfb
                        [ liftM2 (,)
Packit 9a2dfb
                                 (normalG [e|otherwise|])
Packit 9a2dfb
                                 ( varE errorFun
Packit 9a2dfb
                                   `appE` litE (stringL $ show tName)
Packit 9a2dfb
                                   `appE` listE (map ( litE
Packit 9a2dfb
                                                     . stringL
Packit 9a2dfb
                                                     . constructorTagModifier opts
Packit 9a2dfb
                                                     . nameBase
Packit 9a2dfb
                                                     . constructorName
Packit 9a2dfb
                                                     ) cons
Packit 9a2dfb
                                                )
Packit 9a2dfb
                                   `appE` ([|T.unpack|] `appE` varE conKey)
Packit 9a2dfb
                                 )
Packit 9a2dfb
                        ]
Packit 9a2dfb
                      )
Packit 9a2dfb
                      []
Packit 9a2dfb
              ]
Packit 9a2dfb
Packit 9a2dfb
parseNullaryMatches :: Name -> Name -> [Q Match]
Packit 9a2dfb
parseNullaryMatches tName conName =
Packit 9a2dfb
    [ do arr <- newName "arr"
Packit 9a2dfb
         match (conP 'Array [varP arr])
Packit 9a2dfb
               (guardedB
Packit 9a2dfb
                [ liftM2 (,) (normalG $ [|V.null|] `appE` varE arr)
Packit 9a2dfb
                             ([|pure|] `appE` conE conName)
Packit 9a2dfb
                , liftM2 (,) (normalG [|otherwise|])
Packit 9a2dfb
                             (parseTypeMismatch tName conName
Packit 9a2dfb
                                (litE $ stringL "an empty Array")
Packit 9a2dfb
                                (infixApp (litE $ stringL "Array of length ")
Packit 9a2dfb
                                          [|(++)|]
Packit 9a2dfb
                                          ([|show . V.length|] `appE` varE arr)
Packit 9a2dfb
                                )
Packit 9a2dfb
                             )
Packit 9a2dfb
                ]
Packit 9a2dfb
               )
Packit 9a2dfb
               []
Packit 9a2dfb
    , matchFailed tName conName "Array"
Packit 9a2dfb
    ]
Packit 9a2dfb
Packit 9a2dfb
parseUnaryMatches :: JSONClass -> TyVarMap -> Type -> Name -> [Q Match]
Packit 9a2dfb
parseUnaryMatches jc tvMap argTy conName =
Packit 9a2dfb
    [ do arg <- newName "arg"
Packit 9a2dfb
         match (varP arg)
Packit 9a2dfb
               ( normalB $ infixApp (conE conName)
Packit 9a2dfb
                                    [|(<$>)|]
Packit 9a2dfb
                                    (dispatchParseJSON jc conName tvMap argTy
Packit 9a2dfb
                                      `appE` varE arg)
Packit 9a2dfb
               )
Packit 9a2dfb
               []
Packit 9a2dfb
    ]
Packit 9a2dfb
Packit 9a2dfb
parseRecord :: JSONClass
Packit 9a2dfb
            -> TyVarMap
Packit 9a2dfb
            -> [Type]
Packit 9a2dfb
            -> Options
Packit 9a2dfb
            -> Name
Packit 9a2dfb
            -> Name
Packit 9a2dfb
            -> [Name]
Packit 9a2dfb
            -> Name
Packit 9a2dfb
            -> ExpQ
Packit 9a2dfb
parseRecord jc tvMap argTys opts tName conName fields obj =
Packit 9a2dfb
    foldl' (\a b -> infixApp a [|(<*>)|] b)
Packit 9a2dfb
           (infixApp (conE conName) [|(<$>)|] x)
Packit 9a2dfb
           xs
Packit 9a2dfb
    where
Packit 9a2dfb
      x:xs = [ [|lookupField|]
Packit 9a2dfb
               `appE` dispatchParseJSON jc conName tvMap argTy
Packit 9a2dfb
               `appE` litE (stringL $ show tName)
Packit 9a2dfb
               `appE` litE (stringL $ constructorTagModifier opts $ nameBase conName)
Packit 9a2dfb
               `appE` varE obj
Packit 9a2dfb
               `appE` ( [|T.pack|] `appE` stringE (fieldLabel opts field)
Packit 9a2dfb
                      )
Packit 9a2dfb
             | (field, argTy) <- zip fields argTys
Packit 9a2dfb
             ]
Packit 9a2dfb
Packit 9a2dfb
getValField :: Name -> String -> [MatchQ] -> Q Exp
Packit 9a2dfb
getValField obj valFieldName matches = do
Packit 9a2dfb
  val <- newName "val"
Packit 9a2dfb
  doE [ bindS (varP val) $ infixApp (varE obj)
Packit 9a2dfb
                                    [|(.:)|]
Packit 9a2dfb
                                    ([|T.pack|] `appE`
Packit 9a2dfb
                                       litE (stringL valFieldName))
Packit 9a2dfb
      , noBindS $ caseE (varE val) matches
Packit 9a2dfb
      ]
Packit 9a2dfb
Packit 9a2dfb
matchCases :: Either (String, Name) Name -> [MatchQ] -> Q Exp
Packit 9a2dfb
matchCases (Left (valFieldName, obj)) = getValField obj valFieldName
Packit 9a2dfb
matchCases (Right valName)            = caseE (varE valName)
Packit 9a2dfb
Packit 9a2dfb
-- | Generates code to parse the JSON encoding of a single constructor.
Packit 9a2dfb
parseArgs :: JSONClass -- ^ The FromJSON variant being derived.
Packit 9a2dfb
          -> TyVarMap -- ^ Maps the last type variables to their decoding
Packit 9a2dfb
                      --   function arguments.
Packit 9a2dfb
          -> Name -- ^ Name of the type to which the constructor belongs.
Packit 9a2dfb
          -> Options -- ^ Encoding options.
Packit 9a2dfb
          -> ConstructorInfo -- ^ Constructor for which to generate JSON parsing code.
Packit 9a2dfb
          -> Either (String, Name) Name -- ^ Left (valFieldName, objName) or
Packit 9a2dfb
                                        --   Right valName
Packit 9a2dfb
          -> Q Exp
Packit 9a2dfb
-- Nullary constructors.
Packit 9a2dfb
parseArgs _ _ _ _
Packit 9a2dfb
  ConstructorInfo { constructorName    = conName
Packit 9a2dfb
                  , constructorVariant = NormalConstructor
Packit 9a2dfb
                  , constructorFields  = [] }
Packit 9a2dfb
  (Left _) =
Packit 9a2dfb
    [|pure|] `appE` conE conName
Packit 9a2dfb
parseArgs _ _ tName _
Packit 9a2dfb
  ConstructorInfo { constructorName    = conName
Packit 9a2dfb
                  , constructorVariant = NormalConstructor
Packit 9a2dfb
                  , constructorFields  = [] }
Packit 9a2dfb
  (Right valName) =
Packit 9a2dfb
    caseE (varE valName) $ parseNullaryMatches tName conName
Packit 9a2dfb
Packit 9a2dfb
-- Unary constructors.
Packit 9a2dfb
parseArgs jc tvMap _ _
Packit 9a2dfb
  ConstructorInfo { constructorName    = conName
Packit 9a2dfb
                  , constructorVariant = NormalConstructor
Packit 9a2dfb
                  , constructorFields  = [argTy] }
Packit 9a2dfb
  contents = do
Packit 9a2dfb
    argTy' <- resolveTypeSynonyms argTy
Packit 9a2dfb
    matchCases contents $ parseUnaryMatches jc tvMap argTy' conName
Packit 9a2dfb
Packit 9a2dfb
-- Polyadic constructors.
Packit 9a2dfb
parseArgs jc tvMap tName _
Packit 9a2dfb
  ConstructorInfo { constructorName    = conName
Packit 9a2dfb
                  , constructorVariant = NormalConstructor
Packit 9a2dfb
                  , constructorFields  = argTys }
Packit 9a2dfb
  contents = do
Packit 9a2dfb
    argTys' <- mapM resolveTypeSynonyms argTys
Packit 9a2dfb
    let len = genericLength argTys'
Packit 9a2dfb
    matchCases contents $ parseProduct jc tvMap argTys' tName conName len
Packit 9a2dfb
Packit 9a2dfb
-- Records.
Packit 9a2dfb
parseArgs jc tvMap tName opts
Packit 9a2dfb
  ConstructorInfo { constructorName    = conName
Packit 9a2dfb
                  , constructorVariant = RecordConstructor fields
Packit 9a2dfb
                  , constructorFields  = argTys }
Packit 9a2dfb
  (Left (_, obj)) = do
Packit 9a2dfb
    argTys' <- mapM resolveTypeSynonyms argTys
Packit 9a2dfb
    parseRecord jc tvMap argTys' opts tName conName fields obj
Packit 9a2dfb
parseArgs jc tvMap tName opts
Packit 9a2dfb
  info@ConstructorInfo { constructorName    = conName
Packit 9a2dfb
                       , constructorVariant = RecordConstructor fields
Packit 9a2dfb
                       , constructorFields  = argTys }
Packit 9a2dfb
  (Right valName) =
Packit 9a2dfb
    case (unwrapUnaryRecords opts,argTys) of
Packit 9a2dfb
      (True,[_])-> parseArgs jc tvMap tName opts
Packit 9a2dfb
                             (info{constructorVariant = NormalConstructor})
Packit 9a2dfb
                             (Right valName)
Packit 9a2dfb
      _ -> do
Packit 9a2dfb
        obj <- newName "recObj"
Packit 9a2dfb
        argTys' <- mapM resolveTypeSynonyms argTys
Packit 9a2dfb
        caseE (varE valName)
Packit 9a2dfb
          [ match (conP 'Object [varP obj]) (normalB $
Packit 9a2dfb
              parseRecord jc tvMap argTys' opts tName conName fields obj) []
Packit 9a2dfb
          , matchFailed tName conName "Object"
Packit 9a2dfb
          ]
Packit 9a2dfb
Packit 9a2dfb
-- Infix constructors. Apart from syntax these are the same as
Packit 9a2dfb
-- polyadic constructors.
Packit 9a2dfb
parseArgs jc tvMap tName _
Packit 9a2dfb
  ConstructorInfo { constructorName    = conName
Packit 9a2dfb
                  , constructorVariant = InfixConstructor
Packit 9a2dfb
                  , constructorFields  = argTys }
Packit 9a2dfb
  contents = do
Packit 9a2dfb
    argTys' <- mapM resolveTypeSynonyms argTys
Packit 9a2dfb
    matchCases contents $ parseProduct jc tvMap argTys' tName conName 2
Packit 9a2dfb
Packit 9a2dfb
-- | Generates code to parse the JSON encoding of an n-ary
Packit 9a2dfb
-- constructor.
Packit 9a2dfb
parseProduct :: JSONClass -- ^ The FromJSON variant being derived.
Packit 9a2dfb
             -> TyVarMap -- ^ Maps the last type variables to their decoding
Packit 9a2dfb
                         --   function arguments.
Packit 9a2dfb
             -> [Type] -- ^ The argument types of the constructor.
Packit 9a2dfb
             -> Name -- ^ Name of the type to which the constructor belongs.
Packit 9a2dfb
             -> Name -- ^ 'Con'structor name.
Packit 9a2dfb
             -> Integer -- ^ 'Con'structor arity.
Packit 9a2dfb
             -> [Q Match]
Packit 9a2dfb
parseProduct jc tvMap argTys tName conName numArgs =
Packit 9a2dfb
    [ do arr <- newName "arr"
Packit 9a2dfb
         -- List of: "parseJSON (arr `V.unsafeIndex` <IX>)"
Packit 9a2dfb
         let x:xs = [ dispatchParseJSON jc conName tvMap argTy
Packit 9a2dfb
                      `appE`
Packit 9a2dfb
                      infixApp (varE arr)
Packit 9a2dfb
                               [|V.unsafeIndex|]
Packit 9a2dfb
                               (litE $ integerL ix)
Packit 9a2dfb
                    | (argTy, ix) <- zip argTys [0 .. numArgs - 1]
Packit 9a2dfb
                    ]
Packit 9a2dfb
         match (conP 'Array [varP arr])
Packit 9a2dfb
               (normalB $ condE ( infixApp ([|V.length|] `appE` varE arr)
Packit 9a2dfb
                                           [|(==)|]
Packit 9a2dfb
                                           (litE $ integerL numArgs)
Packit 9a2dfb
                                )
Packit 9a2dfb
                                ( foldl' (\a b -> infixApp a [|(<*>)|] b)
Packit 9a2dfb
                                         (infixApp (conE conName) [|(<$>)|] x)
Packit 9a2dfb
                                         xs
Packit 9a2dfb
                                )
Packit 9a2dfb
                                ( parseTypeMismatch tName conName
Packit 9a2dfb
                                    (litE $ stringL $ "Array of length " ++ show numArgs)
Packit 9a2dfb
                                    ( infixApp (litE $ stringL "Array of length ")
Packit 9a2dfb
                                               [|(++)|]
Packit 9a2dfb
                                               ([|show . V.length|] `appE` varE arr)
Packit 9a2dfb
                                    )
Packit 9a2dfb
                                )
Packit 9a2dfb
               )
Packit 9a2dfb
               []
Packit 9a2dfb
    , matchFailed tName conName "Array"
Packit 9a2dfb
    ]
Packit 9a2dfb
Packit 9a2dfb
--------------------------------------------------------------------------------
Packit 9a2dfb
-- Parsing errors
Packit 9a2dfb
--------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
matchFailed :: Name -> Name -> String -> MatchQ
Packit 9a2dfb
matchFailed tName conName expected = do
Packit 9a2dfb
  other <- newName "other"
Packit 9a2dfb
  match (varP other)
Packit 9a2dfb
        ( normalB $ parseTypeMismatch tName conName
Packit 9a2dfb
                      (litE $ stringL expected)
Packit 9a2dfb
                      ([|valueConName|] `appE` varE other)
Packit 9a2dfb
        )
Packit 9a2dfb
        []
Packit 9a2dfb
Packit 9a2dfb
parseTypeMismatch :: Name -> Name -> ExpQ -> ExpQ -> ExpQ
Packit 9a2dfb
parseTypeMismatch tName conName expected actual =
Packit 9a2dfb
    foldl appE
Packit 9a2dfb
          [|parseTypeMismatch'|]
Packit 9a2dfb
          [ litE $ stringL $ nameBase conName
Packit 9a2dfb
          , litE $ stringL $ show tName
Packit 9a2dfb
          , expected
Packit 9a2dfb
          , actual
Packit 9a2dfb
          ]
Packit 9a2dfb
Packit 9a2dfb
class LookupField a where
Packit 9a2dfb
    lookupField :: (Value -> Parser a) -> String -> String
Packit 9a2dfb
                -> Object -> T.Text -> Parser a
Packit 9a2dfb
Packit 9a2dfb
instance OVERLAPPABLE_ LookupField a where
Packit 9a2dfb
    lookupField = lookupFieldWith
Packit 9a2dfb
Packit 9a2dfb
instance INCOHERENT_ LookupField (Maybe a) where
Packit 9a2dfb
    lookupField pj _ _ = parseOptionalFieldWith pj
Packit 9a2dfb
Packit 9a2dfb
instance INCOHERENT_ LookupField (Semigroup.Option a) where
Packit 9a2dfb
    lookupField pj tName rec obj key =
Packit 9a2dfb
        fmap Semigroup.Option
Packit 9a2dfb
             (lookupField (fmap Semigroup.getOption . pj) tName rec obj key)
Packit 9a2dfb
Packit 9a2dfb
lookupFieldWith :: (Value -> Parser a) -> String -> String
Packit 9a2dfb
                -> Object -> T.Text -> Parser a
Packit 9a2dfb
lookupFieldWith pj tName rec obj key =
Packit 9a2dfb
    case H.lookup key obj of
Packit 9a2dfb
      Nothing -> unknownFieldFail tName rec (T.unpack key)
Packit 9a2dfb
      Just v  -> pj v  Key key
Packit 9a2dfb
Packit 9a2dfb
unknownFieldFail :: String -> String -> String -> Parser fail
Packit 9a2dfb
unknownFieldFail tName rec key =
Packit 9a2dfb
    fail $ printf "When parsing the record %s of type %s the key %s was not present."
Packit 9a2dfb
                  rec tName key
Packit 9a2dfb
Packit 9a2dfb
noArrayFail :: String -> String -> Parser fail
Packit 9a2dfb
noArrayFail t o = fail $ printf "When parsing %s expected Array but got %s." t o
Packit 9a2dfb
Packit 9a2dfb
noObjectFail :: String -> String -> Parser fail
Packit 9a2dfb
noObjectFail t o = fail $ printf "When parsing %s expected Object but got %s." t o
Packit 9a2dfb
Packit 9a2dfb
firstElemNoStringFail :: String -> String -> Parser fail
Packit 9a2dfb
firstElemNoStringFail t o = fail $ printf "When parsing %s expected an Array of 2 elements where the first element is a String but got %s at the first element." t o
Packit 9a2dfb
Packit 9a2dfb
wrongPairCountFail :: String -> String -> Parser fail
Packit 9a2dfb
wrongPairCountFail t n =
Packit 9a2dfb
    fail $ printf "When parsing %s expected an Object with a single tag/contents pair but got %s pairs."
Packit 9a2dfb
                  t n
Packit 9a2dfb
Packit 9a2dfb
noStringFail :: String -> String -> Parser fail
Packit 9a2dfb
noStringFail t o = fail $ printf "When parsing %s expected String but got %s." t o
Packit 9a2dfb
Packit 9a2dfb
noMatchFail :: String -> String -> Parser fail
Packit 9a2dfb
noMatchFail t o =
Packit 9a2dfb
    fail $ printf "When parsing %s expected a String with the tag of a constructor but got %s." t o
Packit 9a2dfb
Packit 9a2dfb
not2ElemArray :: String -> Int -> Parser fail
Packit 9a2dfb
not2ElemArray t i = fail $ printf "When parsing %s expected an Array of 2 elements but got %i elements" t i
Packit 9a2dfb
Packit 9a2dfb
conNotFoundFail2ElemArray :: String -> [String] -> String -> Parser fail
Packit 9a2dfb
conNotFoundFail2ElemArray t cs o =
Packit 9a2dfb
    fail $ printf "When parsing %s expected a 2-element Array with a tag and contents element where the tag is one of [%s], but got %s."
Packit 9a2dfb
                  t (intercalate ", " cs) o
Packit 9a2dfb
Packit 9a2dfb
conNotFoundFailObjectSingleField :: String -> [String] -> String -> Parser fail
Packit 9a2dfb
conNotFoundFailObjectSingleField t cs o =
Packit 9a2dfb
    fail $ printf "When parsing %s expected an Object with a single tag/contents pair where the tag is one of [%s], but got %s."
Packit 9a2dfb
                  t (intercalate ", " cs) o
Packit 9a2dfb
Packit 9a2dfb
conNotFoundFailTaggedObject :: String -> [String] -> String -> Parser fail
Packit 9a2dfb
conNotFoundFailTaggedObject t cs o =
Packit 9a2dfb
    fail $ printf "When parsing %s expected an Object with a tag field where the value is one of [%s], but got %s."
Packit 9a2dfb
                  t (intercalate ", " cs) o
Packit 9a2dfb
Packit 9a2dfb
parseTypeMismatch' :: String -> String -> String -> String -> Parser fail
Packit 9a2dfb
parseTypeMismatch' conName tName expected actual =
Packit 9a2dfb
    fail $ printf "When parsing the constructor %s of type %s expected %s but got %s."
Packit 9a2dfb
                  conName tName expected actual
Packit 9a2dfb
Packit 9a2dfb
--------------------------------------------------------------------------------
Packit 9a2dfb
-- Shared ToJSON and FromJSON code
Packit 9a2dfb
--------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
-- | Functionality common to 'deriveJSON', 'deriveJSON1', and 'deriveJSON2'.
Packit 9a2dfb
deriveJSONBoth :: (Options -> Name -> Q [Dec])
Packit 9a2dfb
               -- ^ Function which derives a flavor of 'ToJSON'.
Packit 9a2dfb
               -> (Options -> Name -> Q [Dec])
Packit 9a2dfb
               -- ^ Function which derives a flavor of 'FromJSON'.
Packit 9a2dfb
               -> Options
Packit 9a2dfb
               -- ^ Encoding options.
Packit 9a2dfb
               -> Name
Packit 9a2dfb
               -- ^ Name of the type for which to generate 'ToJSON' and 'FromJSON'
Packit 9a2dfb
               -- instances.
Packit 9a2dfb
               -> Q [Dec]
Packit 9a2dfb
deriveJSONBoth dtj dfj opts name =
Packit 9a2dfb
    liftM2 (++) (dtj opts name) (dfj opts name)
Packit 9a2dfb
Packit 9a2dfb
-- | Functionality common to @deriveToJSON(1)(2)@ and @deriveFromJSON(1)(2)@.
Packit 9a2dfb
deriveJSONClass :: [(JSONFun, JSONClass -> Name -> Options -> [Type]
Packit 9a2dfb
                                        -> [ConstructorInfo] -> Q Exp)]
Packit 9a2dfb
                -- ^ The class methods and the functions which derive them.
Packit 9a2dfb
                -> JSONClass
Packit 9a2dfb
                -- ^ The class for which to generate an instance.
Packit 9a2dfb
                -> Options
Packit 9a2dfb
                -- ^ Encoding options.
Packit 9a2dfb
                -> Name
Packit 9a2dfb
                -- ^ Name of the type for which to generate a class instance
Packit 9a2dfb
                -- declaration.
Packit 9a2dfb
                -> Q [Dec]
Packit 9a2dfb
deriveJSONClass consFuns jc opts name = do
Packit 9a2dfb
  info <- reifyDatatype name
Packit 9a2dfb
  case info of
Packit 9a2dfb
    DatatypeInfo { datatypeContext = ctxt
Packit 9a2dfb
                 , datatypeName    = parentName
Packit 9a2dfb
                 , datatypeVars    = vars
Packit 9a2dfb
                 , datatypeVariant = variant
Packit 9a2dfb
                 , datatypeCons    = cons
Packit 9a2dfb
                 } -> do
Packit 9a2dfb
      (instanceCxt, instanceType)
Packit 9a2dfb
        <- buildTypeInstance parentName jc ctxt vars variant
Packit 9a2dfb
      (:[]) <$> instanceD (return instanceCxt)
Packit 9a2dfb
                          (return instanceType)
Packit 9a2dfb
                          (methodDecs parentName vars cons)
Packit 9a2dfb
  where
Packit 9a2dfb
    methodDecs :: Name -> [Type] -> [ConstructorInfo] -> [Q Dec]
Packit 9a2dfb
    methodDecs parentName vars cons = flip map consFuns $ \(jf, jfMaker) ->
Packit 9a2dfb
      funD (jsonFunValName jf (arity jc))
Packit 9a2dfb
           [ clause []
Packit 9a2dfb
                    (normalB $ jfMaker jc parentName opts vars cons)
Packit 9a2dfb
                    []
Packit 9a2dfb
           ]
Packit 9a2dfb
Packit 9a2dfb
mkFunCommon :: (JSONClass -> Name -> Options -> [Type] -> [ConstructorInfo] -> Q Exp)
Packit 9a2dfb
            -- ^ The function which derives the expression.
Packit 9a2dfb
            -> JSONClass
Packit 9a2dfb
            -- ^ Which class's method is being derived.
Packit 9a2dfb
            -> Options
Packit 9a2dfb
            -- ^ Encoding options.
Packit 9a2dfb
            -> Name
Packit 9a2dfb
            -- ^ Name of the encoded type.
Packit 9a2dfb
            -> Q Exp
Packit 9a2dfb
mkFunCommon consFun jc opts name = do
Packit 9a2dfb
  info <- reifyDatatype name
Packit 9a2dfb
  case info of
Packit 9a2dfb
    DatatypeInfo { datatypeContext = ctxt
Packit 9a2dfb
                 , datatypeName    = parentName
Packit 9a2dfb
                 , datatypeVars    = vars
Packit 9a2dfb
                 , datatypeVariant = variant
Packit 9a2dfb
                 , datatypeCons    = cons
Packit 9a2dfb
                 } -> do
Packit 9a2dfb
      -- We force buildTypeInstance here since it performs some checks for whether
Packit 9a2dfb
      -- or not the provided datatype's kind matches the derived method's
Packit 9a2dfb
      -- typeclass, and produces errors if it can't.
Packit 9a2dfb
      !_ <- buildTypeInstance parentName jc ctxt vars variant
Packit 9a2dfb
      consFun jc parentName opts vars cons
Packit 9a2dfb
Packit 9a2dfb
dispatchFunByType :: JSONClass
Packit 9a2dfb
                  -> JSONFun
Packit 9a2dfb
                  -> Name
Packit 9a2dfb
                  -> TyVarMap
Packit 9a2dfb
                  -> Bool -- True if we are using the function argument that works
Packit 9a2dfb
                          -- on lists (e.g., [a] -> Value). False is we are using
Packit 9a2dfb
                          -- the function argument that works on single values
Packit 9a2dfb
                          -- (e.g., a -> Value).
Packit 9a2dfb
                  -> Type
Packit 9a2dfb
                  -> Q Exp
Packit 9a2dfb
dispatchFunByType _ jf _ tvMap list (VarT tyName) =
Packit 9a2dfb
    varE $ case M.lookup tyName tvMap of
Packit 9a2dfb
                Just (tfjExp, tfjlExp) -> if list then tfjlExp else tfjExp
Packit 9a2dfb
                Nothing                -> jsonFunValOrListName list jf Arity0
Packit 9a2dfb
dispatchFunByType jc jf conName tvMap list (SigT ty _) =
Packit 9a2dfb
    dispatchFunByType jc jf conName tvMap list ty
Packit 9a2dfb
dispatchFunByType jc jf conName tvMap list (ForallT _ _ ty) =
Packit 9a2dfb
    dispatchFunByType jc jf conName tvMap list ty
Packit 9a2dfb
dispatchFunByType jc jf conName tvMap list ty = do
Packit 9a2dfb
    let tyCon :: Type
Packit 9a2dfb
        tyArgs :: [Type]
Packit 9a2dfb
        tyCon :| tyArgs = unapplyTy ty
Packit 9a2dfb
Packit 9a2dfb
        numLastArgs :: Int
Packit 9a2dfb
        numLastArgs = min (arityInt jc) (length tyArgs)
Packit 9a2dfb
Packit 9a2dfb
        lhsArgs, rhsArgs :: [Type]
Packit 9a2dfb
        (lhsArgs, rhsArgs) = splitAt (length tyArgs - numLastArgs) tyArgs
Packit 9a2dfb
Packit 9a2dfb
        tyVarNames :: [Name]
Packit 9a2dfb
        tyVarNames = M.keys tvMap
Packit 9a2dfb
Packit 9a2dfb
    itf <- isTyFamily tyCon
Packit 9a2dfb
    if any (`mentionsName` tyVarNames) lhsArgs
Packit 9a2dfb
          || itf && any (`mentionsName` tyVarNames) tyArgs
Packit 9a2dfb
       then outOfPlaceTyVarError jc conName
Packit 9a2dfb
       else if any (`mentionsName` tyVarNames) rhsArgs
Packit 9a2dfb
            then appsE $ varE (jsonFunValOrListName list jf $ toEnum numLastArgs)
Packit 9a2dfb
                         : zipWith (dispatchFunByType jc jf conName tvMap)
Packit 9a2dfb
                                   (cycle [False,True])
Packit 9a2dfb
                                   (interleave rhsArgs rhsArgs)
Packit 9a2dfb
            else varE $ jsonFunValOrListName list jf Arity0
Packit 9a2dfb
Packit 9a2dfb
dispatchToJSON
Packit 9a2dfb
  :: ToJSONFun -> JSONClass -> Name -> TyVarMap -> Type -> Q Exp
Packit 9a2dfb
dispatchToJSON target jc n tvMap =
Packit 9a2dfb
    dispatchFunByType jc (targetToJSONFun target) n tvMap False
Packit 9a2dfb
Packit 9a2dfb
dispatchParseJSON
Packit 9a2dfb
  :: JSONClass -> Name -> TyVarMap -> Type -> Q Exp
Packit 9a2dfb
dispatchParseJSON  jc n tvMap = dispatchFunByType jc ParseJSON  n tvMap False
Packit 9a2dfb
Packit 9a2dfb
--------------------------------------------------------------------------------
Packit 9a2dfb
-- Utility functions
Packit 9a2dfb
--------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
-- For the given Types, generate an instance context and head.
Packit 9a2dfb
buildTypeInstance :: Name
Packit 9a2dfb
                  -- ^ The type constructor or data family name
Packit 9a2dfb
                  -> JSONClass
Packit 9a2dfb
                  -- ^ The typeclass to derive
Packit 9a2dfb
                  -> Cxt
Packit 9a2dfb
                  -- ^ The datatype context
Packit 9a2dfb
                  -> [Type]
Packit 9a2dfb
                  -- ^ The types to instantiate the instance with
Packit 9a2dfb
                  -> DatatypeVariant
Packit 9a2dfb
                  -- ^ Are we dealing with a data family instance or not
Packit 9a2dfb
                  -> Q (Cxt, Type)
Packit 9a2dfb
buildTypeInstance tyConName jc dataCxt varTysOrig variant = do
Packit 9a2dfb
    -- Make sure to expand through type/kind synonyms! Otherwise, the
Packit 9a2dfb
    -- eta-reduction check might get tripped up over type variables in a
Packit 9a2dfb
    -- synonym that are actually dropped.
Packit 9a2dfb
    -- (See GHC Trac #11416 for a scenario where this actually happened.)
Packit 9a2dfb
    varTysExp <- mapM resolveTypeSynonyms varTysOrig
Packit 9a2dfb
Packit 9a2dfb
    let remainingLength :: Int
Packit 9a2dfb
        remainingLength = length varTysOrig - arityInt jc
Packit 9a2dfb
Packit 9a2dfb
        droppedTysExp :: [Type]
Packit 9a2dfb
        droppedTysExp = drop remainingLength varTysExp
Packit 9a2dfb
Packit 9a2dfb
        droppedStarKindStati :: [StarKindStatus]
Packit 9a2dfb
        droppedStarKindStati = map canRealizeKindStar droppedTysExp
Packit 9a2dfb
Packit 9a2dfb
    -- Check there are enough types to drop and that all of them are either of
Packit 9a2dfb
    -- kind * or kind k (for some kind variable k). If not, throw an error.
Packit 9a2dfb
    when (remainingLength < 0 || elem NotKindStar droppedStarKindStati) $
Packit 9a2dfb
      derivingKindError jc tyConName
Packit 9a2dfb
Packit 9a2dfb
    let droppedKindVarNames :: [Name]
Packit 9a2dfb
        droppedKindVarNames = catKindVarNames droppedStarKindStati
Packit 9a2dfb
Packit 9a2dfb
        -- Substitute kind * for any dropped kind variables
Packit 9a2dfb
        varTysExpSubst :: [Type]
Packit 9a2dfb
        varTysExpSubst = map (substNamesWithKindStar droppedKindVarNames) varTysExp
Packit 9a2dfb
Packit 9a2dfb
        remainingTysExpSubst, droppedTysExpSubst :: [Type]
Packit 9a2dfb
        (remainingTysExpSubst, droppedTysExpSubst) =
Packit 9a2dfb
          splitAt remainingLength varTysExpSubst
Packit 9a2dfb
Packit 9a2dfb
        -- All of the type variables mentioned in the dropped types
Packit 9a2dfb
        -- (post-synonym expansion)
Packit 9a2dfb
        droppedTyVarNames :: [Name]
Packit 9a2dfb
        droppedTyVarNames = freeVariables droppedTysExpSubst
Packit 9a2dfb
Packit 9a2dfb
    -- If any of the dropped types were polykinded, ensure that they are of kind *
Packit 9a2dfb
    -- after substituting * for the dropped kind variables. If not, throw an error.
Packit 9a2dfb
    unless (all hasKindStar droppedTysExpSubst) $
Packit 9a2dfb
      derivingKindError jc tyConName
Packit 9a2dfb
Packit 9a2dfb
    let preds    :: [Maybe Pred]
Packit 9a2dfb
        kvNames  :: [[Name]]
Packit 9a2dfb
        kvNames' :: [Name]
Packit 9a2dfb
        -- Derive instance constraints (and any kind variables which are specialized
Packit 9a2dfb
        -- to * in those constraints)
Packit 9a2dfb
        (preds, kvNames) = unzip $ map (deriveConstraint jc) remainingTysExpSubst
Packit 9a2dfb
        kvNames' = concat kvNames
Packit 9a2dfb
Packit 9a2dfb
        -- Substitute the kind variables specialized in the constraints with *
Packit 9a2dfb
        remainingTysExpSubst' :: [Type]
Packit 9a2dfb
        remainingTysExpSubst' =
Packit 9a2dfb
          map (substNamesWithKindStar kvNames') remainingTysExpSubst
Packit 9a2dfb
Packit 9a2dfb
        -- We now substitute all of the specialized-to-* kind variable names with
Packit 9a2dfb
        -- *, but in the original types, not the synonym-expanded types. The reason
Packit 9a2dfb
        -- we do this is a superficial one: we want the derived instance to resemble
Packit 9a2dfb
        -- the datatype written in source code as closely as possible. For example,
Packit 9a2dfb
        -- for the following data family instance:
Packit 9a2dfb
        --
Packit 9a2dfb
        --   data family Fam a
Packit 9a2dfb
        --   newtype instance Fam String = Fam String
Packit 9a2dfb
        --
Packit 9a2dfb
        -- We'd want to generate the instance:
Packit 9a2dfb
        --
Packit 9a2dfb
        --   instance C (Fam String)
Packit 9a2dfb
        --
Packit 9a2dfb
        -- Not:
Packit 9a2dfb
        --
Packit 9a2dfb
        --   instance C (Fam [Char])
Packit 9a2dfb
        remainingTysOrigSubst :: [Type]
Packit 9a2dfb
        remainingTysOrigSubst =
Packit 9a2dfb
          map (substNamesWithKindStar (droppedKindVarNames `union` kvNames'))
Packit 9a2dfb
            $ take remainingLength varTysOrig
Packit 9a2dfb
Packit 9a2dfb
        isDataFamily :: Bool
Packit 9a2dfb
        isDataFamily = case variant of
Packit 9a2dfb
                         Datatype        -> False
Packit 9a2dfb
                         Newtype         -> False
Packit 9a2dfb
                         DataInstance    -> True
Packit 9a2dfb
                         NewtypeInstance -> True
Packit 9a2dfb
Packit 9a2dfb
        remainingTysOrigSubst' :: [Type]
Packit 9a2dfb
        -- See Note [Kind signatures in derived instances] for an explanation
Packit 9a2dfb
        -- of the isDataFamily check.
Packit 9a2dfb
        remainingTysOrigSubst' =
Packit 9a2dfb
          if isDataFamily
Packit 9a2dfb
             then remainingTysOrigSubst
Packit 9a2dfb
             else map unSigT remainingTysOrigSubst
Packit 9a2dfb
Packit 9a2dfb
        instanceCxt :: Cxt
Packit 9a2dfb
        instanceCxt = catMaybes preds
Packit 9a2dfb
Packit 9a2dfb
        instanceType :: Type
Packit 9a2dfb
        instanceType = AppT (ConT $ jsonClassName jc)
Packit 9a2dfb
                     $ applyTyCon tyConName remainingTysOrigSubst'
Packit 9a2dfb
Packit 9a2dfb
    -- If the datatype context mentions any of the dropped type variables,
Packit 9a2dfb
    -- we can't derive an instance, so throw an error.
Packit 9a2dfb
    when (any (`predMentionsName` droppedTyVarNames) dataCxt) $
Packit 9a2dfb
      datatypeContextError tyConName instanceType
Packit 9a2dfb
    -- Also ensure the dropped types can be safely eta-reduced. Otherwise,
Packit 9a2dfb
    -- throw an error.
Packit 9a2dfb
    unless (canEtaReduce remainingTysExpSubst' droppedTysExpSubst) $
Packit 9a2dfb
      etaReductionError instanceType
Packit 9a2dfb
    return (instanceCxt, instanceType)
Packit 9a2dfb
Packit 9a2dfb
-- | Attempt to derive a constraint on a Type. If successful, return
Packit 9a2dfb
-- Just the constraint and any kind variable names constrained to *.
Packit 9a2dfb
-- Otherwise, return Nothing and the empty list.
Packit 9a2dfb
--
Packit 9a2dfb
-- See Note [Type inference in derived instances] for the heuristics used to
Packit 9a2dfb
-- come up with constraints.
Packit 9a2dfb
deriveConstraint :: JSONClass -> Type -> (Maybe Pred, [Name])
Packit 9a2dfb
deriveConstraint jc t
Packit 9a2dfb
  | not (isTyVar t) = (Nothing, [])
Packit 9a2dfb
  | hasKindStar t   = (Just (applyCon (jcConstraint Arity0) tName), [])
Packit 9a2dfb
  | otherwise = case hasKindVarChain 1 t of
Packit 9a2dfb
      Just ns | jcArity >= Arity1
Packit 9a2dfb
              -> (Just (applyCon (jcConstraint Arity1) tName), ns)
Packit 9a2dfb
      _ -> case hasKindVarChain 2 t of
Packit 9a2dfb
           Just ns | jcArity == Arity2
Packit 9a2dfb
                   -> (Just (applyCon (jcConstraint Arity2) tName), ns)
Packit 9a2dfb
           _ -> (Nothing, [])
Packit 9a2dfb
  where
Packit 9a2dfb
    tName :: Name
Packit 9a2dfb
    tName = varTToName t
Packit 9a2dfb
Packit 9a2dfb
    jcArity :: Arity
Packit 9a2dfb
    jcArity = arity jc
Packit 9a2dfb
Packit 9a2dfb
    jcConstraint :: Arity -> Name
Packit 9a2dfb
    jcConstraint = jsonClassName . JSONClass (direction jc)
Packit 9a2dfb
Packit 9a2dfb
{-
Packit 9a2dfb
Note [Kind signatures in derived instances]
Packit 9a2dfb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Packit 9a2dfb
Packit 9a2dfb
It is possible to put explicit kind signatures into the derived instances, e.g.,
Packit 9a2dfb
Packit 9a2dfb
  instance C a => C (Data (f :: * -> *)) where ...
Packit 9a2dfb
Packit 9a2dfb
But it is preferable to avoid this if possible. If we come up with an incorrect
Packit 9a2dfb
kind signature (which is entirely possible, since Template Haskell doesn't always
Packit 9a2dfb
have the best track record with reifying kind signatures), then GHC will flat-out
Packit 9a2dfb
reject the instance, which is quite unfortunate.
Packit 9a2dfb
Packit 9a2dfb
Plain old datatypes have the advantage that you can avoid using any kind signatures
Packit 9a2dfb
at all in their instances. This is because a datatype declaration uses all type
Packit 9a2dfb
variables, so the types that we use in a derived instance uniquely determine their
Packit 9a2dfb
kinds. As long as we plug in the right types, the kind inferencer can do the rest
Packit 9a2dfb
of the work. For this reason, we use unSigT to remove all kind signatures before
Packit 9a2dfb
splicing in the instance context and head.
Packit 9a2dfb
Packit 9a2dfb
Data family instances are trickier, since a data family can have two instances that
Packit 9a2dfb
are distinguished by kind alone, e.g.,
Packit 9a2dfb
Packit 9a2dfb
  data family Fam (a :: k)
Packit 9a2dfb
  data instance Fam (a :: * -> *)
Packit 9a2dfb
  data instance Fam (a :: *)
Packit 9a2dfb
Packit 9a2dfb
If we dropped the kind signatures for C (Fam a), then GHC will have no way of
Packit 9a2dfb
knowing which instance we are talking about. To avoid this scenario, we always
Packit 9a2dfb
include explicit kind signatures in data family instances. There is a chance that
Packit 9a2dfb
the inferred kind signatures will be incorrect, but if so, we can always fall back
Packit 9a2dfb
on the mk- functions.
Packit 9a2dfb
Packit 9a2dfb
Note [Type inference in derived instances]
Packit 9a2dfb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Packit 9a2dfb
Packit 9a2dfb
Type inference is can be tricky to get right, and we want to avoid recreating the
Packit 9a2dfb
entirety of GHC's type inferencer in Template Haskell. For this reason, we will
Packit 9a2dfb
probably never come up with derived instance contexts that are as accurate as
Packit 9a2dfb
GHC's. But that doesn't mean we can't do anything! There are a couple of simple
Packit 9a2dfb
things we can do to make instance contexts that work for 80% of use cases:
Packit 9a2dfb
Packit 9a2dfb
1. If one of the last type parameters is polykinded, then its kind will be
Packit 9a2dfb
   specialized to * in the derived instance. We note what kind variable the type
Packit 9a2dfb
   parameter had and substitute it with * in the other types as well. For example,
Packit 9a2dfb
   imagine you had
Packit 9a2dfb
Packit 9a2dfb
     data Data (a :: k) (b :: k)
Packit 9a2dfb
Packit 9a2dfb
   Then you'd want to derived instance to be:
Packit 9a2dfb
Packit 9a2dfb
     instance C (Data (a :: *))
Packit 9a2dfb
Packit 9a2dfb
   Not:
Packit 9a2dfb
Packit 9a2dfb
     instance C (Data (a :: k))
Packit 9a2dfb
Packit 9a2dfb
2. We naïvely come up with instance constraints using the following criteria:
Packit 9a2dfb
Packit 9a2dfb
   (i)   If there's a type parameter n of kind *, generate a ToJSON n/FromJSON n
Packit 9a2dfb
         constraint.
Packit 9a2dfb
   (ii)  If there's a type parameter n of kind k1 -> k2 (where k1/k2 are * or kind
Packit 9a2dfb
         variables), then generate a ToJSON1 n/FromJSON1 n constraint, and if
Packit 9a2dfb
         k1/k2 are kind variables, then substitute k1/k2 with * elsewhere in the
Packit 9a2dfb
         types. We must consider the case where they are kind variables because
Packit 9a2dfb
         you might have a scenario like this:
Packit 9a2dfb
Packit 9a2dfb
           newtype Compose (f :: k2 -> *) (g :: k1 -> k2) (a :: k1)
Packit 9a2dfb
             = Compose (f (g a))
Packit 9a2dfb
Packit 9a2dfb
         Which would have a derived ToJSON1 instance of:
Packit 9a2dfb
Packit 9a2dfb
           instance (ToJSON1 f, ToJSON1 g) => ToJSON1 (Compose f g) where ...
Packit 9a2dfb
   (iii) If there's a type parameter n of kind k1 -> k2 -> k3 (where k1/k2/k3 are
Packit 9a2dfb
         * or kind variables), then generate a ToJSON2 n/FromJSON2 n constraint
Packit 9a2dfb
         and perform kind substitution as in the other cases.
Packit 9a2dfb
-}
Packit 9a2dfb
Packit 9a2dfb
checkExistentialContext :: JSONClass -> TyVarMap -> Cxt -> Name
Packit 9a2dfb
                        -> Q a -> Q a
Packit 9a2dfb
checkExistentialContext jc tvMap ctxt conName q =
Packit 9a2dfb
  if (any (`predMentionsName` M.keys tvMap) ctxt
Packit 9a2dfb
       || M.size tvMap < arityInt jc)
Packit 9a2dfb
       && not (allowExQuant jc)
Packit 9a2dfb
     then existentialContextError conName
Packit 9a2dfb
     else q
Packit 9a2dfb
Packit 9a2dfb
{-
Packit 9a2dfb
Note [Matching functions with GADT type variables]
Packit 9a2dfb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Packit 9a2dfb
Packit 9a2dfb
When deriving ToJSON2, there is a tricky corner case to consider:
Packit 9a2dfb
Packit 9a2dfb
  data Both a b where
Packit 9a2dfb
    BothCon :: x -> x -> Both x x
Packit 9a2dfb
Packit 9a2dfb
Which encoding functions should be applied to which arguments of BothCon?
Packit 9a2dfb
We have a choice, since both the function of type (a -> Value) and of type
Packit 9a2dfb
(b -> Value) can be applied to either argument. In such a scenario, the
Packit 9a2dfb
second encoding function takes precedence over the first encoding function, so the
Packit 9a2dfb
derived ToJSON2 instance would be something like:
Packit 9a2dfb
Packit 9a2dfb
  instance ToJSON2 Both where
Packit 9a2dfb
    liftToJSON2 tj1 tj2 p (BothCon x1 x2) = Array $ create $ do
Packit 9a2dfb
      mv <- unsafeNew 2
Packit 9a2dfb
      unsafeWrite mv 0 (tj1 x1)
Packit 9a2dfb
      unsafeWrite mv 1 (tj2 x2)
Packit 9a2dfb
      return mv
Packit 9a2dfb
Packit 9a2dfb
This is not an arbitrary choice, as this definition ensures that
Packit 9a2dfb
liftToJSON2 toJSON = liftToJSON for a derived ToJSON1 instance for
Packit 9a2dfb
Both.
Packit 9a2dfb
-}
Packit 9a2dfb
Packit 9a2dfb
-- A mapping of type variable Names to their encoding/decoding function Names.
Packit 9a2dfb
-- For example, in a ToJSON2 declaration, a TyVarMap might look like
Packit 9a2dfb
--
Packit 9a2dfb
-- { a ~> (tj1, tjl1)
Packit 9a2dfb
-- , b ~> (tj2, tjl2) }
Packit 9a2dfb
--
Packit 9a2dfb
-- where a and b are the last two type variables of the datatype, tj1 and tjl1 are
Packit 9a2dfb
-- the function arguments of types (a -> Value) and ([a] -> Value), and tj2 and tjl2
Packit 9a2dfb
-- are the function arguments of types (b -> Value) and ([b] -> Value).
Packit 9a2dfb
type TyVarMap = Map Name (Name, Name)
Packit 9a2dfb
Packit 9a2dfb
-- | Returns True if a Type has kind *.
Packit 9a2dfb
hasKindStar :: Type -> Bool
Packit 9a2dfb
hasKindStar VarT{}         = True
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,8,0)
Packit 9a2dfb
hasKindStar (SigT _ StarT) = True
Packit 9a2dfb
#else
Packit 9a2dfb
hasKindStar (SigT _ StarK) = True
Packit 9a2dfb
#endif
Packit 9a2dfb
hasKindStar _              = False
Packit 9a2dfb
Packit 9a2dfb
-- Returns True is a kind is equal to *, or if it is a kind variable.
Packit 9a2dfb
isStarOrVar :: Kind -> Bool
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,8,0)
Packit 9a2dfb
isStarOrVar StarT  = True
Packit 9a2dfb
isStarOrVar VarT{} = True
Packit 9a2dfb
#else
Packit 9a2dfb
isStarOrVar StarK  = True
Packit 9a2dfb
#endif
Packit 9a2dfb
isStarOrVar _      = False
Packit 9a2dfb
Packit 9a2dfb
-- Generate a list of fresh names with a common prefix, and numbered suffixes.
Packit 9a2dfb
newNameList :: String -> Int -> Q [Name]
Packit 9a2dfb
newNameList prefix len = mapM newName [prefix ++ show n | n <- [1..len]]
Packit 9a2dfb
Packit 9a2dfb
-- | @hasKindVarChain n kind@ Checks if @kind@ is of the form
Packit 9a2dfb
-- k_0 -> k_1 -> ... -> k_(n-1), where k0, k1, ..., and k_(n-1) can be * or
Packit 9a2dfb
-- kind variables.
Packit 9a2dfb
hasKindVarChain :: Int -> Type -> Maybe [Name]
Packit 9a2dfb
hasKindVarChain kindArrows t =
Packit 9a2dfb
  let uk = uncurryKind (tyKind t)
Packit 9a2dfb
  in if (NE.length uk - 1 == kindArrows) && F.all isStarOrVar uk
Packit 9a2dfb
        then Just (concatMap freeVariables uk)
Packit 9a2dfb
        else Nothing
Packit 9a2dfb
Packit 9a2dfb
-- | If a Type is a SigT, returns its kind signature. Otherwise, return *.
Packit 9a2dfb
tyKind :: Type -> Kind
Packit 9a2dfb
tyKind (SigT _ k) = k
Packit 9a2dfb
tyKind _          = starK
Packit 9a2dfb
Packit 9a2dfb
-- | Extract Just the Name from a type variable. If the argument Type is not a
Packit 9a2dfb
-- type variable, return Nothing.
Packit 9a2dfb
varTToNameMaybe :: Type -> Maybe Name
Packit 9a2dfb
varTToNameMaybe (VarT n)   = Just n
Packit 9a2dfb
varTToNameMaybe (SigT t _) = varTToNameMaybe t
Packit 9a2dfb
varTToNameMaybe _          = Nothing
Packit 9a2dfb
Packit 9a2dfb
-- | Extract the Name from a type variable. If the argument Type is not a
Packit 9a2dfb
-- type variable, throw an error.
Packit 9a2dfb
varTToName :: Type -> Name
Packit 9a2dfb
varTToName = fromMaybe (error "Not a type variable!") . varTToNameMaybe
Packit 9a2dfb
Packit 9a2dfb
interleave :: [a] -> [a] -> [a]
Packit 9a2dfb
interleave (a1:a1s) (a2:a2s) = a1:a2:interleave a1s a2s
Packit 9a2dfb
interleave _        _        = []
Packit 9a2dfb
Packit 9a2dfb
-- | Fully applies a type constructor to its type variables.
Packit 9a2dfb
applyTyCon :: Name -> [Type] -> Type
Packit 9a2dfb
applyTyCon = foldl' AppT . ConT
Packit 9a2dfb
Packit 9a2dfb
-- | Is the given type a variable?
Packit 9a2dfb
isTyVar :: Type -> Bool
Packit 9a2dfb
isTyVar (VarT _)   = True
Packit 9a2dfb
isTyVar (SigT t _) = isTyVar t
Packit 9a2dfb
isTyVar _          = False
Packit 9a2dfb
Packit 9a2dfb
-- | Is the given type a type family constructor (and not a data family constructor)?
Packit 9a2dfb
isTyFamily :: Type -> Q Bool
Packit 9a2dfb
isTyFamily (ConT n) = do
Packit 9a2dfb
    info <- reify n
Packit 9a2dfb
    return $ case info of
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,11,0)
Packit 9a2dfb
         FamilyI OpenTypeFamilyD{} _       -> True
Packit 9a2dfb
#else
Packit 9a2dfb
         FamilyI (FamilyD TypeFam _ _ _) _ -> True
Packit 9a2dfb
#endif
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,9,0)
Packit 9a2dfb
         FamilyI ClosedTypeFamilyD{} _     -> True
Packit 9a2dfb
#endif
Packit 9a2dfb
         _ -> False
Packit 9a2dfb
isTyFamily _ = return False
Packit 9a2dfb
Packit 9a2dfb
-- | Peel off a kind signature from a Type (if it has one).
Packit 9a2dfb
unSigT :: Type -> Type
Packit 9a2dfb
unSigT (SigT t _) = t
Packit 9a2dfb
unSigT t          = t
Packit 9a2dfb
Packit 9a2dfb
-- | Are all of the items in a list (which have an ordering) distinct?
Packit 9a2dfb
--
Packit 9a2dfb
-- This uses Set (as opposed to nub) for better asymptotic time complexity.
Packit 9a2dfb
allDistinct :: Ord a => [a] -> Bool
Packit 9a2dfb
allDistinct = allDistinct' Set.empty
Packit 9a2dfb
  where
Packit 9a2dfb
    allDistinct' :: Ord a => Set a -> [a] -> Bool
Packit 9a2dfb
    allDistinct' uniqs (x:xs)
Packit 9a2dfb
        | x `Set.member` uniqs = False
Packit 9a2dfb
        | otherwise            = allDistinct' (Set.insert x uniqs) xs
Packit 9a2dfb
    allDistinct' _ _           = True
Packit 9a2dfb
Packit 9a2dfb
-- | Does the given type mention any of the Names in the list?
Packit 9a2dfb
mentionsName :: Type -> [Name] -> Bool
Packit 9a2dfb
mentionsName = go
Packit 9a2dfb
  where
Packit 9a2dfb
    go :: Type -> [Name] -> Bool
Packit 9a2dfb
    go (AppT t1 t2) names = go t1 names || go t2 names
Packit 9a2dfb
    go (SigT t _k)  names = go t names
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,8,0)
Packit 9a2dfb
                              || go _k names
Packit 9a2dfb
#endif
Packit 9a2dfb
    go (VarT n)     names = n `elem` names
Packit 9a2dfb
    go _            _     = False
Packit 9a2dfb
Packit 9a2dfb
-- | Does an instance predicate mention any of the Names in the list?
Packit 9a2dfb
predMentionsName :: Pred -> [Name] -> Bool
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,10,0)
Packit 9a2dfb
predMentionsName = mentionsName
Packit 9a2dfb
#else
Packit 9a2dfb
predMentionsName (ClassP n tys) names = n `elem` names || any (`mentionsName` names) tys
Packit 9a2dfb
predMentionsName (EqualP t1 t2) names = mentionsName t1 names || mentionsName t2 names
Packit 9a2dfb
#endif
Packit 9a2dfb
Packit 9a2dfb
-- | Split an applied type into its individual components. For example, this:
Packit 9a2dfb
--
Packit 9a2dfb
-- @
Packit 9a2dfb
-- Either Int Char
Packit 9a2dfb
-- @
Packit 9a2dfb
--
Packit 9a2dfb
-- would split to this:
Packit 9a2dfb
--
Packit 9a2dfb
-- @
Packit 9a2dfb
-- [Either, Int, Char]
Packit 9a2dfb
-- @
Packit 9a2dfb
unapplyTy :: Type -> NonEmpty Type
Packit 9a2dfb
unapplyTy = NE.reverse . go
Packit 9a2dfb
  where
Packit 9a2dfb
    go :: Type -> NonEmpty Type
Packit 9a2dfb
    go (AppT t1 t2)    = t2 <| go t1
Packit 9a2dfb
    go (SigT t _)      = go t
Packit 9a2dfb
    go (ForallT _ _ t) = go t
Packit 9a2dfb
    go t               = t :| []
Packit 9a2dfb
Packit 9a2dfb
-- | Split a type signature by the arrows on its spine. For example, this:
Packit 9a2dfb
--
Packit 9a2dfb
-- @
Packit 9a2dfb
-- forall a b. (a ~ b) => (a -> b) -> Char -> ()
Packit 9a2dfb
-- @
Packit 9a2dfb
--
Packit 9a2dfb
-- would split to this:
Packit 9a2dfb
--
Packit 9a2dfb
-- @
Packit 9a2dfb
-- (a ~ b, [a -> b, Char, ()])
Packit 9a2dfb
-- @
Packit 9a2dfb
uncurryTy :: Type -> (Cxt, NonEmpty Type)
Packit 9a2dfb
uncurryTy (AppT (AppT ArrowT t1) t2) =
Packit 9a2dfb
  let (ctxt, tys) = uncurryTy t2
Packit 9a2dfb
  in (ctxt, t1 <| tys)
Packit 9a2dfb
uncurryTy (SigT t _) = uncurryTy t
Packit 9a2dfb
uncurryTy (ForallT _ ctxt t) =
Packit 9a2dfb
  let (ctxt', tys) = uncurryTy t
Packit 9a2dfb
  in (ctxt ++ ctxt', tys)
Packit 9a2dfb
uncurryTy t = ([], t :| [])
Packit 9a2dfb
Packit 9a2dfb
-- | Like uncurryType, except on a kind level.
Packit 9a2dfb
uncurryKind :: Kind -> NonEmpty Kind
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,8,0)
Packit 9a2dfb
uncurryKind = snd . uncurryTy
Packit 9a2dfb
#else
Packit 9a2dfb
uncurryKind (ArrowK k1 k2) = k1 <| uncurryKind k2
Packit 9a2dfb
uncurryKind k              = k :| []
Packit 9a2dfb
#endif
Packit 9a2dfb
Packit 9a2dfb
createKindChain :: Int -> Kind
Packit 9a2dfb
createKindChain = go starK
Packit 9a2dfb
  where
Packit 9a2dfb
    go :: Kind -> Int -> Kind
Packit 9a2dfb
    go k 0 = k
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,8,0)
Packit 9a2dfb
    go k !n = go (AppT (AppT ArrowT StarT) k) (n - 1)
Packit 9a2dfb
#else
Packit 9a2dfb
    go k !n = go (ArrowK StarK k) (n - 1)
Packit 9a2dfb
#endif
Packit 9a2dfb
Packit 9a2dfb
-- | Makes a string literal expression from a constructor's name.
Packit 9a2dfb
conNameExp :: Options -> ConstructorInfo -> Q Exp
Packit 9a2dfb
conNameExp opts = litE
Packit 9a2dfb
                . stringL
Packit 9a2dfb
                . constructorTagModifier opts
Packit 9a2dfb
                . nameBase
Packit 9a2dfb
                . constructorName
Packit 9a2dfb
Packit 9a2dfb
-- | Extracts a record field label.
Packit 9a2dfb
fieldLabel :: Options -- ^ Encoding options
Packit 9a2dfb
           -> Name
Packit 9a2dfb
           -> String
Packit 9a2dfb
fieldLabel opts = fieldLabelModifier opts . nameBase
Packit 9a2dfb
Packit 9a2dfb
-- | The name of the outermost 'Value' constructor.
Packit 9a2dfb
valueConName :: Value -> String
Packit 9a2dfb
valueConName (Object _) = "Object"
Packit 9a2dfb
valueConName (Array  _) = "Array"
Packit 9a2dfb
valueConName (String _) = "String"
Packit 9a2dfb
valueConName (Number _) = "Number"
Packit 9a2dfb
valueConName (Bool   _) = "Boolean"
Packit 9a2dfb
valueConName Null       = "Null"
Packit 9a2dfb
Packit 9a2dfb
applyCon :: Name -> Name -> Pred
Packit 9a2dfb
applyCon con t =
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,10,0)
Packit 9a2dfb
          AppT (ConT con) (VarT t)
Packit 9a2dfb
#else
Packit 9a2dfb
          ClassP con [VarT t]
Packit 9a2dfb
#endif
Packit 9a2dfb
Packit 9a2dfb
-- | Checks to see if the last types in a data family instance can be safely eta-
Packit 9a2dfb
-- reduced (i.e., dropped), given the other types. This checks for three conditions:
Packit 9a2dfb
--
Packit 9a2dfb
-- (1) All of the dropped types are type variables
Packit 9a2dfb
-- (2) All of the dropped types are distinct
Packit 9a2dfb
-- (3) None of the remaining types mention any of the dropped types
Packit 9a2dfb
canEtaReduce :: [Type] -> [Type] -> Bool
Packit 9a2dfb
canEtaReduce remaining dropped =
Packit 9a2dfb
       all isTyVar dropped
Packit 9a2dfb
    && allDistinct droppedNames -- Make sure not to pass something of type [Type], since Type
Packit 9a2dfb
                                -- didn't have an Ord instance until template-haskell-2.10.0.0
Packit 9a2dfb
    && not (any (`mentionsName` droppedNames) remaining)
Packit 9a2dfb
  where
Packit 9a2dfb
    droppedNames :: [Name]
Packit 9a2dfb
    droppedNames = map varTToName dropped
Packit 9a2dfb
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
-- Expanding type synonyms
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
applySubstitutionKind :: Map Name Kind -> Type -> Type
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,8,0)
Packit 9a2dfb
applySubstitutionKind = applySubstitution
Packit 9a2dfb
#else
Packit 9a2dfb
applySubstitutionKind _ t = t
Packit 9a2dfb
#endif
Packit 9a2dfb
Packit 9a2dfb
substNameWithKind :: Name -> Kind -> Type -> Type
Packit 9a2dfb
substNameWithKind n k = applySubstitutionKind (M.singleton n k)
Packit 9a2dfb
Packit 9a2dfb
substNamesWithKindStar :: [Name] -> Type -> Type
Packit 9a2dfb
substNamesWithKindStar ns t = foldr' (`substNameWithKind` starK) t ns
Packit 9a2dfb
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
-- Error messages
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
-- | Either the given data type doesn't have enough type variables, or one of
Packit 9a2dfb
-- the type variables to be eta-reduced cannot realize kind *.
Packit 9a2dfb
derivingKindError :: JSONClass -> Name -> Q a
Packit 9a2dfb
derivingKindError jc tyConName = fail
Packit 9a2dfb
  . showString "Cannot derive well-kinded instance of form ‘"
Packit 9a2dfb
  . showString className
Packit 9a2dfb
  . showChar ' '
Packit 9a2dfb
  . showParen True
Packit 9a2dfb
    ( showString (nameBase tyConName)
Packit 9a2dfb
    . showString " ..."
Packit 9a2dfb
    )
Packit 9a2dfb
  . showString "‘\n\tClass "
Packit 9a2dfb
  . showString className
Packit 9a2dfb
  . showString " expects an argument of kind "
Packit 9a2dfb
  . showString (pprint . createKindChain $ arityInt jc)
Packit 9a2dfb
  $ ""
Packit 9a2dfb
  where
Packit 9a2dfb
    className :: String
Packit 9a2dfb
    className = nameBase $ jsonClassName jc
Packit 9a2dfb
Packit 9a2dfb
-- | One of the last type variables cannot be eta-reduced (see the canEtaReduce
Packit 9a2dfb
-- function for the criteria it would have to meet).
Packit 9a2dfb
etaReductionError :: Type -> Q a
Packit 9a2dfb
etaReductionError instanceType = fail $
Packit 9a2dfb
    "Cannot eta-reduce to an instance of form \n\tinstance (...) => "
Packit 9a2dfb
    ++ pprint instanceType
Packit 9a2dfb
Packit 9a2dfb
-- | The data type has a DatatypeContext which mentions one of the eta-reduced
Packit 9a2dfb
-- type variables.
Packit 9a2dfb
datatypeContextError :: Name -> Type -> Q a
Packit 9a2dfb
datatypeContextError dataName instanceType = fail
Packit 9a2dfb
    . showString "Can't make a derived instance of ‘"
Packit 9a2dfb
    . showString (pprint instanceType)
Packit 9a2dfb
    . showString "‘:\n\tData type ‘"
Packit 9a2dfb
    . showString (nameBase dataName)
Packit 9a2dfb
    . showString "‘ must not have a class context involving the last type argument(s)"
Packit 9a2dfb
    $ ""
Packit 9a2dfb
Packit 9a2dfb
-- | The data type mentions one of the n eta-reduced type variables in a place other
Packit 9a2dfb
-- than the last nth positions of a data type in a constructor's field.
Packit 9a2dfb
outOfPlaceTyVarError :: JSONClass -> Name -> a
Packit 9a2dfb
outOfPlaceTyVarError jc conName = error
Packit 9a2dfb
    . showString "Constructor ‘"
Packit 9a2dfb
    . showString (nameBase conName)
Packit 9a2dfb
    . showString "‘ must only use its last "
Packit 9a2dfb
    . shows n
Packit 9a2dfb
    . showString " type variable(s) within the last "
Packit 9a2dfb
    . shows n
Packit 9a2dfb
    . showString " argument(s) of a data type"
Packit 9a2dfb
    $ ""
Packit 9a2dfb
  where
Packit 9a2dfb
    n :: Int
Packit 9a2dfb
    n = arityInt jc
Packit 9a2dfb
Packit 9a2dfb
-- | The data type has an existential constraint which mentions one of the
Packit 9a2dfb
-- eta-reduced type variables.
Packit 9a2dfb
existentialContextError :: Name -> a
Packit 9a2dfb
existentialContextError conName = error
Packit 9a2dfb
  . showString "Constructor ‘"
Packit 9a2dfb
  . showString (nameBase conName)
Packit 9a2dfb
  . showString "‘ must be truly polymorphic in the last argument(s) of the data type"
Packit 9a2dfb
  $ ""
Packit 9a2dfb
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
-- Class-specific constants
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
-- | A representation of the arity of the ToJSON/FromJSON typeclass being derived.
Packit 9a2dfb
data Arity = Arity0 | Arity1 | Arity2
Packit 9a2dfb
  deriving (Enum, Eq, Ord)
Packit 9a2dfb
Packit 9a2dfb
-- | Whether ToJSON(1)(2) or FromJSON(1)(2) is being derived.
Packit 9a2dfb
data Direction = To | From
Packit 9a2dfb
Packit 9a2dfb
-- | A representation of which typeclass method is being spliced in.
Packit 9a2dfb
data JSONFun = ToJSON | ToEncoding | ParseJSON
Packit 9a2dfb
Packit 9a2dfb
-- | A refinement of JSONFun to [ToJSON, ToEncoding].
Packit 9a2dfb
data ToJSONFun = Value | Encoding
Packit 9a2dfb
Packit 9a2dfb
targetToJSONFun :: ToJSONFun -> JSONFun
Packit 9a2dfb
targetToJSONFun Value = ToJSON
Packit 9a2dfb
targetToJSONFun Encoding = ToEncoding
Packit 9a2dfb
Packit 9a2dfb
-- | A representation of which typeclass is being derived.
Packit 9a2dfb
data JSONClass = JSONClass { direction :: Direction, arity :: Arity }
Packit 9a2dfb
Packit 9a2dfb
toJSONClass, toJSON1Class, toJSON2Class,
Packit 9a2dfb
    fromJSONClass, fromJSON1Class, fromJSON2Class :: JSONClass
Packit 9a2dfb
toJSONClass    = JSONClass To   Arity0
Packit 9a2dfb
toJSON1Class   = JSONClass To   Arity1
Packit 9a2dfb
toJSON2Class   = JSONClass To   Arity2
Packit 9a2dfb
fromJSONClass  = JSONClass From Arity0
Packit 9a2dfb
fromJSON1Class = JSONClass From Arity1
Packit 9a2dfb
fromJSON2Class = JSONClass From Arity2
Packit 9a2dfb
Packit 9a2dfb
jsonClassName :: JSONClass -> Name
Packit 9a2dfb
jsonClassName (JSONClass To   Arity0) = ''ToJSON
Packit 9a2dfb
jsonClassName (JSONClass To   Arity1) = ''ToJSON1
Packit 9a2dfb
jsonClassName (JSONClass To   Arity2) = ''ToJSON2
Packit 9a2dfb
jsonClassName (JSONClass From Arity0) = ''FromJSON
Packit 9a2dfb
jsonClassName (JSONClass From Arity1) = ''FromJSON1
Packit 9a2dfb
jsonClassName (JSONClass From Arity2) = ''FromJSON2
Packit 9a2dfb
Packit 9a2dfb
jsonFunValName :: JSONFun -> Arity -> Name
Packit 9a2dfb
jsonFunValName ToJSON     Arity0 = 'toJSON
Packit 9a2dfb
jsonFunValName ToJSON     Arity1 = 'liftToJSON
Packit 9a2dfb
jsonFunValName ToJSON     Arity2 = 'liftToJSON2
Packit 9a2dfb
jsonFunValName ToEncoding Arity0 = 'toEncoding
Packit 9a2dfb
jsonFunValName ToEncoding Arity1 = 'liftToEncoding
Packit 9a2dfb
jsonFunValName ToEncoding Arity2 = 'liftToEncoding2
Packit 9a2dfb
jsonFunValName ParseJSON  Arity0 = 'parseJSON
Packit 9a2dfb
jsonFunValName ParseJSON  Arity1 = 'liftParseJSON
Packit 9a2dfb
jsonFunValName ParseJSON  Arity2 = 'liftParseJSON2
Packit 9a2dfb
Packit 9a2dfb
jsonFunListName :: JSONFun -> Arity -> Name
Packit 9a2dfb
jsonFunListName ToJSON     Arity0 = 'toJSONList
Packit 9a2dfb
jsonFunListName ToJSON     Arity1 = 'liftToJSONList
Packit 9a2dfb
jsonFunListName ToJSON     Arity2 = 'liftToJSONList2
Packit 9a2dfb
jsonFunListName ToEncoding Arity0 = 'toEncodingList
Packit 9a2dfb
jsonFunListName ToEncoding Arity1 = 'liftToEncodingList
Packit 9a2dfb
jsonFunListName ToEncoding Arity2 = 'liftToEncodingList2
Packit 9a2dfb
jsonFunListName ParseJSON  Arity0 = 'parseJSONList
Packit 9a2dfb
jsonFunListName ParseJSON  Arity1 = 'liftParseJSONList
Packit 9a2dfb
jsonFunListName ParseJSON  Arity2 = 'liftParseJSONList2
Packit 9a2dfb
Packit 9a2dfb
jsonFunValOrListName :: Bool -- e.g., toJSONList if True, toJSON if False
Packit 9a2dfb
                     -> JSONFun -> Arity -> Name
Packit 9a2dfb
jsonFunValOrListName False = jsonFunValName
Packit 9a2dfb
jsonFunValOrListName True  = jsonFunListName
Packit 9a2dfb
Packit 9a2dfb
arityInt :: JSONClass -> Int
Packit 9a2dfb
arityInt = fromEnum . arity
Packit 9a2dfb
Packit 9a2dfb
allowExQuant :: JSONClass -> Bool
Packit 9a2dfb
allowExQuant (JSONClass To _) = True
Packit 9a2dfb
allowExQuant _                = False
Packit 9a2dfb
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
-- StarKindStatus
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
-- | Whether a type is not of kind *, is of kind *, or is a kind variable.
Packit 9a2dfb
data StarKindStatus = NotKindStar
Packit 9a2dfb
                    | KindStar
Packit 9a2dfb
                    | IsKindVar Name
Packit 9a2dfb
  deriving Eq
Packit 9a2dfb
Packit 9a2dfb
-- | Does a Type have kind * or k (for some kind variable k)?
Packit 9a2dfb
canRealizeKindStar :: Type -> StarKindStatus
Packit 9a2dfb
canRealizeKindStar t = case t of
Packit 9a2dfb
    _ | hasKindStar t -> KindStar
Packit 9a2dfb
#if MIN_VERSION_template_haskell(2,8,0)
Packit 9a2dfb
    SigT _ (VarT k) -> IsKindVar k
Packit 9a2dfb
#endif
Packit 9a2dfb
    _ -> NotKindStar
Packit 9a2dfb
Packit 9a2dfb
-- | Returns 'Just' the kind variable 'Name' of a 'StarKindStatus' if it exists.
Packit 9a2dfb
-- Otherwise, returns 'Nothing'.
Packit 9a2dfb
starKindStatusToName :: StarKindStatus -> Maybe Name
Packit 9a2dfb
starKindStatusToName (IsKindVar n) = Just n
Packit 9a2dfb
starKindStatusToName _             = Nothing
Packit 9a2dfb
Packit 9a2dfb
-- | Concat together all of the StarKindStatuses that are IsKindVar and extract
Packit 9a2dfb
-- the kind variables' Names out.
Packit 9a2dfb
catKindVarNames :: [StarKindStatus] -> [Name]
Packit 9a2dfb
catKindVarNames = mapMaybe starKindStatusToName