使用幻像类型强制模糊实例

时间:2014-05-18 22:37:52

标签: haskell

我正在使用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)]

(我尝试过功能依赖和类型系列,但这似乎也没有帮助)。

1 个答案:

答案 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之前的所有内容,您可以在其中对所涉及的类型变量编写约束。