Generic 通过什么机制与 Aeson 的 ToJSON class 交互?

By what mechanism does Generic interact with Aeson's ToJSON class?

查看 servant example 的一部分,我看到:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}

module Main where

import Prelude ()
import Prelude.Compat

import Control.Monad.Except
import Control.Monad.Reader
import Data.Aeson.Types
import Data.Attoparsec.ByteString
import Data.ByteString (ByteString)
import Data.List
import Data.Maybe
import Data.String.Conversions
import Data.Time.Calendar
import GHC.Generics
import Lucid
import Network.HTTP.Media ((//), (/:))
import Network.Wai
import Network.Wai.Handler.Warp
import Servant
import System.Directory
import Text.Blaze
import Text.Blaze.Html.Renderer.Utf8
import qualified Data.Aeson.Parser
import qualified Text.Blaze.Html

type UserAPI1 = "users" :> Get '[JSON] [User]

data User = User
  { name :: String
  , age :: Int
  , email :: String
  , registration_date :: Day
  } deriving (Eq, Show, Generic)

instance ToJSON User

当我删除 Genericderiving 时,出现以下错误:

• No instance for (Generic User)
    arising from a use of ‘aeson-1.1.2.0:Data.Aeson.Types.ToJSON.$dmtoJSON’

因此,我假设 UserGeneric 类型类实例使 instance ToJSON User 能够创建一个 JSON Encoder User.

instance ToJSON User 的机制是什么,即类型签名,如果这是正确的词?

我试图从 stack ghci 查看它的类型,即 REPL,但失败了:

λ: >:t instance
<interactive>:1:1: error: parse error on input ‘instance’
λ: >:i instance
<interactive>:1:1: error: parse error on input ‘instance’

让我们看看 the source ToJSON:

class ToJSON a where
    -- | Convert a Haskell value to a JSON-friendly intermediate type.
    toJSON     :: a -> Value

    default toJSON :: (Generic a, GToJSON Zero (Rep a)) => a -> Value
    toJSON = genericToJSON defaultOptions

ToJSON class 有一个默认的 toJSON 实现,带有额外的类型约束(包括 Generic,如您所见)。这需要 DefaultSignatures 扩展;注意在那个模块的顶部你可以看到

{-# LANGUAGE DefaultSignatures #-}

另一个约束 GToJSON Zero (Rep a)a 的结构施加了一些进一步的限制,因此不是 每个 类型 Generic 实例将满足此签名。

关于您关于 GHCi 的问题:instance 是一个 Haskell 关键字。检查 toJSON 可能是您想要的。这将向您显示我们在源代码中看到的相同信息:

λ> :i toJSON
class ToJSON a where
  toJSON :: a -> Value
  default toJSON :: (GHC.Generics.Generic a,
                     GToJSON Zero (GHC.Generics.Rep a)) =>
                    a -> Value
  ...
        -- Defined in ‘aeson-1.1.2.0:Data.Aeson.Types.ToJSON’