我正在使用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
这不起作用,因为编译器无法推断出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
如果我在(String, String)
类型签名
queryT
替换了幻像类型
queryT :: Connection -> QueryT a -> IO [(String, String)]
事情再次奏效。那么区别是什么呢 ?
为什么类型推断不能推断出行的类型为[(String, String)]
?
(我尝试过功能依赖和类型系列,但这似乎也没有帮助)。
答案 0 :(得分:3)
问题不在于为调用 (String, String)
的行类型推断queryT
;推断得很好。
问题在于编译queryT
本身;这是a
中的多态性所以编译器不是假定来推断(String, String)
,它应该编译适用于任何类型{{ 1}}。但是a
通过调用queryT
得到了结果,而query_
根本无法用于任何类型,只适用于query_
类型类中的类型。 / p>
但是您不希望能够在声称生成QueryResult
的查询上运行queryT
,因为行类型Either (IO [a -> Int]) (Maybe Void)
希望将其作为可能的行类型,因此解决方案是限制query_
对幻像类型在queryT
中的查询进行操作:
QueryResult
这正是将queryT :: QueryResult a => Connection -> QueryT a -> IO [a]
添加到QueryResult a
的上下文所建议的“可能修复”的含义。类型签名的“上下文”是queryT
之前的所有内容,您可以在其中对所涉及的类型变量编写约束。