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

huangapple go评论88阅读模式
英文:

Inspecting records whose fields' types are the result of type-level computations

问题

  1. 在[servant](https://hackage.haskell.org/package/servant-server)库的背景下提出了这个问题,但在[其他上下文](https://gitlab.haskell.org/ghc/ghc/-/wikis/implementing-trees-that-grow/trees-that-grow-guidance)中也出现了这个问题。
  2. [Servant][1]允许您使用记录定义[命名路由][2],就像这样:
  3. ```haskell
  4. {-# LANGUAGE DataKinds #-}
  5. {-# LANGUAGE DeriveGeneric #-}
  6. {-# LANGUAGE DerivingStrategies #-}
  7. {-# LANGUAGE TypeOperators #-}
  8. import GHC.Generics
  9. import Servant
  10. type API = NamedRoutes Counter
  11. data Counter mode = Counter
  12. { counterPost :: mode :- Capture "stuff" Int :> PostNoContent,
  13. counterGet :: mode :- Get '[JSON] Int
  14. }
  15. deriving stock (Generic)

类型Server API将执行一些类型级计算,该计算评估为类型

  1. ghci> :kind! Server API
  2. Server API :: *
  3. = Counter (AsServerT Handler)

我希望有一种方法可以“窥视”记录类型并检查每个字段的最终类型,这里将是评估 AsServerT Handler :- Capture "stuff" Int :> PostNoContentAsServerT Handler :- Get '[JSON] Int 的结果。

但是分别指定这两个表达式很不方便。我想将类型 Server API 传递给...某物,并得到所有字段评估后的类型。是否存在这样的功能?

  1. <details>
  2. <summary>英文:</summary>
  3. This came up in the context of the [servant](https://hackage.haskell.org/package/servant-server) library, but the issue reappears in [other contexts](https://gitlab.haskell.org/ghc/ghc/-/wikis/implementing-trees-that-grow/trees-that-grow-guidance).
  4. [Servant][1] allows you to define [named routes][2] using a record, like this:
  5. ```haskell
  6. {-# LANGUAGE DataKinds #-}
  7. {-# LANGUAGE DeriveGeneric #-}
  8. {-# LANGUAGE DerivingStrategies #-}
  9. {-# LANGUAGE TypeOperators #-}
  10. import GHC.Generics
  11. import Servant
  12. type API = NamedRoutes Counter
  13. data Counter mode = Counter
  14. { counterPost :: mode :- Capture &quot;stuff&quot; Int :&gt; PostNoContent,
  15. counterGet :: mode :- Get &#39;[JSON] Int
  16. }
  17. deriving stock (Generic)

The type Server API will perform some type-level computation, which evaluates to the type

  1. ghci&gt; :kind! Server API
  2. Server API :: *
  3. = Counter (AsServerT Handler)

I would like a way to "peek into" the record type and inspect the final types of each field, which here would be the result of evaluating AsServerT Handler :- Capture &quot;stuff&quot; Int :&gt; PostNoContent and AsServerT Handler :- Get &#39;[JSON] Int.

But specifying those two expressions separatedly is inconvenient. I would like to pass the type Server API to... something, and get the evaluated type of all fields in return. Does such functionality exist?

答案1

得分: 4

It seems that one way of getting the fields' types is through the generic representation:

  1. ghci> :kind! Rep (Server API)
  2. Rep (Server API) :: * -> *
  3. = M1
  4. D
  5. ('MetaData "Counter" "Main" "main" 'False)
  6. (M1
  7. C
  8. ('MetaCons "Counter" 'PrefixI 'True)
  9. (M1
  10. S
  11. ('MetaSel
  12. ('Just "counterPost")
  13. 'NoSourceUnpackedness
  14. 'NoSourceStrictness
  15. 'DecidedLazy)
  16. (K1 R (Int -> Handler NoContent))
  17. :*: M1
  18. S
  19. ('MetaSel
  20. ('Just "counterGet")
  21. 'NoSourceUnpackedness
  22. 'NoSourceStrictness
  23. 'DecidedLazy)
  24. (K1 R (Handler Int))))

For less verbosity, a Generics-based helper could produce a more manageable output. Using my by-other-names package, we can define:

  1. recordFields ::
  2. forall r.
  3. (Generic r, GHasFieldNames (Rep r), GRecord Typeable (Rep r)) =>
  4. [(String, TypeRep)]
  5. recordFields =
  6. Data.Foldable.toList $
  7. gRecordEnum @Typeable @(Rep r) gGetFieldNames typeRep

Which, put to use:

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

英文:

It seems that one way of getting the fields' types is through the generic representation:

  1. ghci&gt; :kind! Rep (Server API)
  2. Rep (Server API) :: * -&gt; *
  3. = M1
  4. D
  5. (&#39;MetaData &quot;Counter&quot; &quot;Main&quot; &quot;main&quot; &#39;False)
  6. (M1
  7. C
  8. (&#39;MetaCons &quot;Counter&quot; &#39;PrefixI &#39;True)
  9. (M1
  10. S
  11. (&#39;MetaSel
  12. (&#39;Just &quot;counterPost&quot;)
  13. &#39;NoSourceUnpackedness
  14. &#39;NoSourceStrictness
  15. &#39;DecidedLazy)
  16. (K1 R (Int -&gt; Handler NoContent))
  17. :*: M1
  18. S
  19. (&#39;MetaSel
  20. (&#39;Just &quot;counterGet&quot;)
  21. &#39;NoSourceUnpackedness
  22. &#39;NoSourceStrictness
  23. &#39;DecidedLazy)
  24. (K1 R (Handler Int))))

Kind of verbose, but it works and plays well with the "Eval" code lens in VSCode:

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

For less verbosity, a Generics-based helper could produce a more manageable output. Using my by-other-names package, we can define:

  1. recordFields ::
  2. forall r.
  3. (Generic r, GHasFieldNames (Rep r), GRecord Typeable (Rep r)) =&gt;
  4. [(String, TypeRep)]
  5. recordFields =
  6. Data.Foldable.toList $
  7. gRecordEnum @Typeable @(Rep r) gGetFieldNames typeRep

Which, put to use:

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

huangapple
  • 本文由 发表于 2023年4月11日 01:56:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/75979484.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定