英文:
Using `generics-sop` to get type metadata info at compile time
问题
我迄今为止一直很成功地使用 `generics-sop`,但有一个用例,我想要获取记录字段的名称。
我正在处理的类型是乘积类型,我可以使用约束 `IsProductType a xs` 来强制执行这一点。
这样做,我最终会得到一堆类似以下的代码:
```haskell
import qualified Generics.SOP as SOP
import Generics.SOP (...)
f :: forall a xs. IsProductType a xs => ...
f = ... where
datatypeInfo :: DatatypeInfo (SOP.Code a)
datatypeInfo = SOP.datatypeInfo (Proxy :: Proxy a)
constructorsInfo :: NP ConstructorInfo (Code a)
constructorsInfo = SOP.constructorInfo datatypeInfo
constructorInfo :: ConstructorInfo xs
-- This is safe due to the (IsProductType a xs) constraint, as there is only one constructor
constructorInfo = hd constructorsInfo
fieldsInfo :: NP FieldInfo xs
fieldsInfo = let Record _ fields = y in fields
但在最后一行,我收到一个关于 Record
匹配不完整的警告。实际上,并非如此,ConstructorInfo
实际上有三个构造函数,而 Record
只匹配其中一个。
然后我意识到,我的代码可能会针对任何乘积类型编译,即使它们不是记录格式。我宁愿不要有不完整的匹配,但如果我能在 a
上放置一个约束,确保它是记录,那么不完整的匹配将永远不会在运行时失败,我会接受它。
我认为问题在于函数 datatypeInfo:
datatypeInfo :: proxy a -> DatatypeInfo (Code a)
移除了类型级别的信息,因为 Code a
,作为类型级别的列表列表,不再具有关于 a
是否具有命名记录字段的信息。
是否有其他使用 generics-sop
的方法来保留该信息?我非常喜欢 generics-sop
,尤其是不必处理二叉树的总和/乘积组合,只需处理这个,所以如果 generics-sop
在编译时和运行时都暴露字段是否命名的信息,那将是很好的。
<details>
<summary>英文:</summary>
I've been using `generics-sop` quite successfully so far, but for one use case I'd like to get the names of record fields.
The types I'm working with are product types, and I can use the constraint `IsProductType a xs` to enforce this.
Doing so I end up with a bunch of code like the following:
import qualified Generics.SOP as SOP
import Generics.SOP (...)
f :: forall a xs. IsProductType a xs => ...
f = ... where
datatypeInfo :: DatatypeInfo (SOP.Code a)
datatypeInfo = SOP.datatypeInfo (Proxy :: Proxy a)
constructorsInfo :: NP ConstructorInfo (Code a)
constructorsInfo = SOP.constructorInfo datatypeInfo
constructorInfo :: ConstructorInfo xs
-- This is safe due to the (IsProductType a xs) constraint, as there is only one constructor
constructorInfo = hd constructorsInfo
fieldsInfo :: NP FieldInfo xs
fieldsInfo = let Record _ fields = y in fields
But at the last line I get a warning about the `Record` match not being exhaustive. And it isn't, `ConstructorInfo` actually has three constructors, and `Record` is only matching one of them.
So then I realised that my code probably will compile against any product type, even those not in record format. I'd rather not have the incomplete match, but I'd be okay with it if I could somehow put a constraint on `a` that ensured that it was a record, so the incomplete match will never fail at runtime.
I think the issue is that the function [datatypeInfo](https://hackage.haskell.org/package/generics-sop-0.5.1.2/docs/Generics-SOP.html#v:datatypeInfo):
datatypeInfo :: proxy a -> DatatypeInfo (Code a)
Removes type level information, because `Code a`, being a type level list of lists, no longer has the information about whether `a` has named record fields or not.
Is there something else I can do using `generics-sop` to retain that information. I quite like `generic-sop`, in particular not having to deal with binary trees of sum/product combinations but just dealing with this, so it would be nice if `generics-sop` exposed the information about whether a field was named or not at compile time as well as runtime.
</details>
# 答案1
**得分**: 1
回答我自己的问题,我认为[records-sop][1]解决了这个问题。
[1]: https://hackage.haskell.org/package/records-sop-0.1.1.0
<details>
<summary>英文:</summary>
Answering my own question, I think [records-sop][1] solves this issue.
[1]: https://hackage.haskell.org/package/records-sop-0.1.1.0
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论