我正在使用mysql-simple
,并且我尝试使用幻像类型以避免必须使用显式类型签名。
假设我正在尝试执行以下查询SELECT Firstname, Lastname FROM users
。
我会尝试这样的事情:
{-# LANGUAGE OverloadedStrings #-}
import Database.MySQL.Simple
myQuery :: Query
myQuery = "SELECT firstname, lastname FROM users"
main = do
conn <-connect defaultConnectInfo
rows <- queryT conn myQuery
mapM_ print rows
这不起作用,因为编译器无法推断出行
的类型。解决方案是添加如下类型签名:
{-# LANGUAGE OverloadedStrings #-}
import Database.MySQL.Simple
myQuery :: Query
myQuery = "SELECT firstname, lastname FROM users"
main = do
conn <-connect defaultConnectInfo
rows <- queryT conn myQuery
mapM_ print (rows :: [(String, String)]) ------<< Here
编译器推断出每一行都是(String, String)
并且一切正常。
然而,这个解决方案并不令人满意,因为如果我修改 myQuery
我需要修改 rows
类型签名。而且,这是一个简化的示例。在实际代码中,查询来自查询组合器(它保存行类型),因此无法对行类型进行硬编码。
我尝试使用幻像类型来使用类型化查询
{-# LANGUAGE OverloadedStrings #-}
import Database.MySQL.Simple
data QueryT a = QueryT Query ---------------<< Phantom type
queryT :: Connection -> QueryT a -> IO [a]
queryT conn (QueryT q) = query_ conn q
myQuery :: QueryT (String, String) ----------<< Query holding it's row type
myQuery = QueryT $ "SELECT firstname, lastname FROM users"
main = do
conn <-connect defaultConnectInfo
rows <- queryT conn myQuery
mapM_ print rows
这行不通。我收到以下消息:
stack.hs:7:26:
No instance for (Database.MySQL.Simple.QueryResults.QueryResults a)
arising from a use of `query_'
Possible fix:
add (Database.MySQL.Simple.QueryResults.QueryResults
a) to the context of
the type signature for queryT :: Connection -> QueryT a -> IO [a]
In the expression: query_ conn q
In an equation for `queryT': queryT conn (QueryT q) = query_ conn q
如果我在 queryT
类型签名中将幻像类型替换为 (String, String)
queryT :: Connection -> QueryT a -> IO [(String, String)]
一切又恢复正常了。那么区别是什么呢 ?
为什么类型推断无法推断出 rows 的类型为 [(String, String)]
?
(我尝试过函数依赖和类型族,但这似乎也没有帮助)。
最佳答案
问题不在于为您调用 queryT
的行类型推断 (String, String)
;就可以很好地推断出。
问题在于编译 queryT
本身;这是 a
中的多态性,因此编译器不应该推断 (String, String)
,它应该编译适用于 < em>任何类型a
。但是 queryT
是通过调用 query_
来获取结果的,而 query_
根本无法对任何类型起作用,只能对QueryResult
类型类。
但是您不希望能够在声称生成 Either (IO [a -> Int]) (Maybe Void)
的查询上运行 queryT
作为行类型超过 query_
希望将其作为可能的行类型,因此解决方案是限制 queryT
对幻像类型位于 QueryResult< 中的查询进行操作
也是:
queryT :: QueryResult a => Connection -> QueryT a -> IO [a]
这正是建议的“可能的修复”的含义,即将 QueryResult a
添加到 queryT
的上下文中。类型签名的“上下文”是 =>
之前的所有内容,您可以在其中编写对所涉及的类型变量的约束。
关于haskell - 强制使用幻像类型的不明确实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23727582/