我正在尝试扩展(或试图找出是否可以扩展)具有类型签名的函数已经达到我的知识极限,因为我使用的库暴露了非常多态的类型和API,更具多态性。
我正在使用persistent
和hsqml
,两个图书馆都有很多毛茸茸的类型,至少对我而言。
我需要将persistent
个实体包装到hsqml
“代理”对象,以便公开QML
(基本上是javascript)。
为此,我给自己做了一个叫做getStandardClassMembers
的助手。你这样使用它:
instance DefaultClass (Entity Project) where
classMembers = getStandardClassMembers
[
("name", projectName),
("hasCustomIcon", text . not . BS.null . projectIcon),
("hasDev", projectHasDev), -- TODO bool as string, ugly..
("hasUat", projectHasUat),
("hasStaging", projectHasStage),
("hasProd", projectHasProd)
]
[]
getStandardClassMembers
的定义是there。用我指定的成员定义一个javascript对象,并调用我给出的haskell函数来实现JS对象的成员。
这很不错但有一个'但是'。对中第二个位置的函数必须返回Text
。在这里你可以看到,对于除第一个函数之外的所有函数,我宁愿返回Bool
。理想情况下,我会使getStandardClassMembers
的类型签名采用更多态的类型,不要求第二个函数必然返回Text
。我再一次不知道是否可能,但我决定试一试。
所以我将RankNTypes
添加到该文件的已经庞大的语言扩展列表中(由于我选择的库,我有点被迫进入,但其他方面很棒)。
我改变了:
getStandardClassMembers :: (Marshal tr, ToBackendKey SqlBackend record, Typeable record,
MarshalMode tr ICanReturnTo () ~ Yes) =>
[(String, record -> tr)] -> [(String, ObjRef (Entity record) -> Maybe Int)]
-> [Member (GetObjType (ObjRef (Entity record)))]
要:
getStandardClassMembers :: (Marshal tr, ToBackendKey SqlBackend record, Typeable record,
MarshalMode tr ICanReturnTo () ~ Yes) =>
[(String, forall tr. record -> tr)] -> [(String, ObjRef (Entity record) -> Maybe Int)]
-> [Member (GetObjType (ObjRef (Entity record)))]
(所以我添加了forall tr.
)
我得到了:
Illegal polymorphic or qualified type: forall tr. record -> tr
Perhaps you intended to use ImpredicativeTypes
现在我对这些事情了解得足以知道启用ImpredicativeTypes
并不是一个好主意。另外,如果我启用它,它也不起作用。
我想要的是什么,或者我应该休息一下,感谢任何与那些已经非常混乱的类型签名和语言扩展集合有关的东西吗?
编辑可能正确的解决方法是忘记我的助手并使用hsqml
基本API返回基础知识。但是否则我仍然很好奇这是否可能。
答案 0 :(得分:1)
我认为基本问题是你试图将不同类型的值放在一个列表中。即使ImpredicativeTypes
也不能真正允许你以你需要的方式做到这一点。存在类型可以使用包装器,但我认为它们也是矫枉过正的。
相反,我建议根据你实际上使用元组的方式,从插入元组到插入总是相同类型的东西。查看链接的代码,我看到了
\(name, f) -> defPropertyConst name (return . f . entityVal . fromObjRef)
作为您在元组中使用的函数,以从中获取实际所需的内容。那么为什么不为此定义一个全局函数呢?
stdMember name f = defPropertyConst name (return . f . entityVal . fromObjRef)
(这个名字只是一个建议;你可能想要更短的东西,甚至是操作员。)
然后改变getStandardClassMembers
以获取这些值的列表作为其第一个参数,并且可能类似于第二个参数。
假设没有更多的类型细微之处,那么你应该能够写
instance DefaultClass (Entity Project) where
classMembers = getStandardClassMembers
[
stdMember "name" projectName,
stdMember "hasCustomIcon" $ text . not . BS.null . projectIcon,
stdMember "hasDev" projectHasDev, -- TODO bool as string, ugly..
stdMember "hasUat" projectHasUat,
stdMember "hasStaging" projectHasStage,
stdMember "hasProd" projectHasProd
]
[]