没有类型变量约束的类型类中的值

Nat*_*ade 2 haskell types typeclass

我正在使用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 = ...
    ...
Run Code Online (Sandbox Code Playgroud)

现在这里的问题是saveCollection,因为它不使用GHC不会让它编译的任何类型变量,但是它对数据库函数(例如getFromDatabase)非常重要,以便它们知道要保存到哪个集合.

问题是,如何在类型变量没有绑定的类型类中有一个值.

dfe*_*uer 5

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

  saveCollection :: proxy a -> Text
  -- Note the `proxy` is lower case

instance DatabaseType MyDB where
  saveCollection _ = "MyDB"
Run Code Online (Sandbox Code Playgroud)

现在使用它,你可能会这样做:

import Data.Proxy

foo = saveCollection (Proxy :: Proxy MyDB)
Run Code Online (Sandbox Code Playgroud)

在方法声明中使用小写的原因是方便:您可以使用任何类型具有正确形式的值,而不是Proxy MyDB,如果您碰巧在呼叫站点上有一个值.


在某些情况下,标准代理技术可能会导致共享丢失.发生这种情况是因为函数调用的结果通常不会被记忆.在这种情况下,您可以使用标记类型.Data.Tagged定义

newtype Tagged s b = Tagged {unTagged :: b}
Run Code Online (Sandbox Code Playgroud)

标记类型比代理更难以处理,除非您使用部分类型签名或显式类型应用程序,这是GHC最近添加的两个功能.但是,如果你想,你可以写

saveCollection :: Tagged a Text
Run Code Online (Sandbox Code Playgroud)

然后在实例中,

saveCollection = Tagged "Hi there."
Run Code Online (Sandbox Code Playgroud)

直接使用它需要类似的东西

unTagged (saveCollection :: Tagged MyDB Text)
Run Code Online (Sandbox Code Playgroud)

或者,部分类型签名,

unTagged (saveCollection :: Tagged MyDB _)
Run Code Online (Sandbox Code Playgroud)

或者使用显式类型应用程序我认为类似

unTagged (saveCollection@MyDB)
Run Code Online (Sandbox Code Playgroud)

由于这种尴尬,该tagged软件包提供了在基于代理和标记的表示之间进行转换的功能.