类型类中的值,不带类型变量约束



我正在使用Happstack开发一个Web应用程序,我正在编写一些代码来将我的类型存储在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(非常重要,以便它们知道要保存到哪个集合。

问题是,如何在类型类中具有不受类型变量绑定的值。

您必须添加类型变量。最简单的方法是使用代理:

  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包提供了在基于代理的表示和标记的表示之间转换的功能。

最新更新