我正在使用Happstack开发一个webapp,我正在编写一些代码来将我的类型存储在MongoDB中。我想通过将代码放入类型类来缩短我的代码,以便我可以使用相同的代码来读取和写入不同类型的数据库。像这样:
class DatabaseType a where
toDoc :: a -> Document
fromDoc :: Document -> a
saveCollection :: Text
getFromDatabase :: (MonadIO m) => Pipe -> Text -> Value -> m a
getFromDatabase pipe field value = ...
...
现在这里的问题是saveCollection
,因为它不使用GHC不会让它编译的任何类型变量,但是它对数据库函数非常重要(比如getFromDatabase
)以便他们知道要保存的集合。
问题是,如何在类型变量没有绑定的类型类中有一个值。
答案 0 :(得分:5)
您必须添加类型变量。最简单的方法是使用代理:
saveCollection :: proxy a -> Text
-- Note the `proxy` is lower case
instance DatabaseType MyDB where
saveCollection _ = "MyDB"
现在使用它,你可能会这样做:
import Data.Proxy
foo = saveCollection (Proxy :: Proxy MyDB)
在方法声明中使用小写的原因是方便:如果你碰巧在呼叫站点有一个值,你可以使用任何类型具有正确形式的值而不是Proxy MyDB
。
在某些情况下,标准代理技术可能会导致共享丢失。发生这种情况是因为函数调用的结果通常不会被记忆。在这种情况下,您可以使用标记的类型。 Data.Tagged
定义
newtype Tagged s b = Tagged {unTagged :: b}
标记类型比代理更难以处理,除非您使用部分类型签名或显式类型应用程序,这是GHC最近添加的两个功能。但是,如果你想,你可以写
saveCollection :: Tagged a Text
然后在实例中,
saveCollection = Tagged "Hi there."
直接使用它需要像
这样的东西unTagged (saveCollection :: Tagged MyDB Text)
或者,部分类型签名,
unTagged (saveCollection :: Tagged MyDB _)
或使用显式类型应用程序我认为类似于
unTagged (saveCollection@MyDB)
由于这种尴尬,tagged
包提供了在基于代理和标记的表示之间进行转换的功能。