代码之家  ›  专栏  ›  技术社区  ›  danidiaz

检查字段类型是类型级计算结果的记录

  •  0
  • danidiaz  · 技术社区  · 2 年前

    这是在 servant 图书馆,但问题再次出现在 other contexts

    Servant 允许您定义 named routes 使用记录,如下所示:

    {-# LANGUAGE DataKinds #-}
    {-# LANGUAGE DeriveGeneric #-}
    {-# LANGUAGE DerivingStrategies #-}
    {-# LANGUAGE TypeOperators #-}
    import GHC.Generics
    import Servant
    
    type API = NamedRoutes Counter
    
    data Counter mode = Counter
      { counterPost :: mode :- Capture "stuff" Int :> PostNoContent,
        counterGet :: mode :- Get '[JSON] Int
      }
      deriving stock (Generic)
    

    类型 Server API 将执行某种类型级别的计算,计算结果为

    ghci> :kind! Server API
    Server API :: *
    = Counter (AsServerT Handler)
    

    我想要一种“窥探”记录类型并检查每个字段的最终类型的方法,这将是评估的结果 AsServerT Handler :- Capture "stuff" Int :> PostNoContent AsServerT Handler :- Get '[JSON] Int

    但是,单独指定这两个表达式是不方便的。我想通过打字 服务器API to…something,并获取所有字段的求值类型作为回报。是否存在这样的功能?

    1 回复  |  直到 2 年前
        1
  •  4
  •   danidiaz    2 年前

    似乎获取字段类型的一种方法是通过泛型表示:

    ghci> :kind! Rep (Server API)
    Rep (Server API) :: * -> *
    = M1
        D
        ('MetaData "Counter" "Main" "main" 'False)
        (M1
           C
           ('MetaCons "Counter" 'PrefixI 'True)
           (M1
              S
              ('MetaSel
                 ('Just "counterPost")
                 'NoSourceUnpackedness
                 'NoSourceStrictness
                 'DecidedLazy)
              (K1 R (Int -> Handler NoContent))
            :*: M1
                  S
                  ('MetaSel
                     ('Just "counterGet")
                     'NoSourceUnpackedness
                     'NoSourceStrictness
                     'DecidedLazy)
                  (K1 R (Handler Int))))
    

    有点冗长,但它能很好地与 "Eval" code lens 在VSCode中:

    enter image description here

    为了减少冗长 Generics -基于helper可以生成更易于管理的输出。使用我的 by-other-names 包,我们可以定义:

    recordFields ::
      forall r.
      (Generic r, GHasFieldNames (Rep r), GRecord Typeable (Rep r)) =>
      [(String, TypeRep)]
    recordFields =
      Data.Foldable.toList $
        gRecordEnum @Typeable @(Rep r) gGetFieldNames typeRep
    

    哪个,投入使用:

    enter image description here