在以下代码中,我对foo
的返回值所做的一切都是使用其Show
接口
foo :: String
foo = "asdf"
main = do
print foo
有没有办法修改foo
的类型签名来强制执行此操作,禁止人们将字符串视为字符列表,只允许他们通过抽象类型类接口访问它?我认为对于存在类型可能是可能的,但我不知道如何。
我想这样做的原因是因为在HDBC数据库库中,所有功能都可用作IConnection
类型类的方法。我正在编写创建数据库连接的函数,我想知道是否可能使其返回类型反映抽象接口,而不是返回具体的Sqlite3 Connection
。
现在我搜索了一下,我发现有一个ConnWrapper数据类型可用,但我仍然无法理解这一切是如何组合在一起的。返回ConnWrapper是唯一的出路吗?
答案 0 :(得分:7)
对于存在类型确实可能,但它很可能是bad idea。
使用存在类型的方式是ConnWrapper
,就像你说的那样:
data ConnWrapper = forall conn. IConnection conn => ConnWrapper conn
这种类型相对简单:它是一个IConnection
字典以及与之兼容的类型的值。如果x :: conn
和conn
是IConnection
的实例,那么ConnWrapper x :: ConnWrapper
。给定y :: ConnWrapper
,除了它是IConnection
的实例之外,你对它的值的类型一无所知。所以你可以说case y of ConnWrapper z -> disconnect z
,但你不能说case y of ConnWrapper z -> z
- 你不能给它任何Haskell类型。
(ConnWrapper
也是IConnection
的直接实例,由于该类非常简单,这意味着您几乎不需要直接在ConnWrapper
上进行模式匹配。您可以只需使用disconnect y
等等。)
如果您正在制作API,我强烈建议您重新考虑我上面链接的帖子,或至少理解这种方法。在Haskell中使用ExistentialTypes实际上比非存在方法更复杂。例如,您应该看到为什么data Foo = forall a. Show a => Foo a
(几乎)等同于String
。看起来HDBC可能会以这种方式使用,但是......