4

我看过一些类似主题的帖子,但它们并没有真正帮助我解决我的问题。所以我敢重复。

现在我有一个带有签名的函数:

run' :: Expr query => RethinkDBHandle -> query -> IO [JSON]

这是一个数据库查询运行功能。

我将此函数包装在一个池中(池已创建且与问题无关)以简化连接。

rdb q = withResource pool (\h -> run' (use h $ db "test") q)

本质上,此函数与上面的运行具有完全相同的签名。

问题是,如果我使用没有签名的函数,那么一切都很好,GHC 很乐意解决问题。一旦我指定签名,它就会停止处理某些输入,抱怨无法推断出类型。

主要有两种输入类型用作查询输入。

ReQL and Table

这两种类型都是实例,Expr因此它们都被 GHC 接受。

一旦我输入签名,一切都停止工作,GHC 抱怨无法推断类型并给我“类型签名绑定的刚性类型变量”错误。如果我让签名更具体,ReQL而不是Expr a,那么显然它会停止接受Table输入,反之亦然。将输入指定为Expr a和的实例,会因上述错误而停止ReQLTable将签名全部删除可以正常工作。

那么我该如何解决呢?放弃签名感觉不对。

我不知道我是否应该使问题更通用或更具体,但如果它有帮助,这是一个包含所有类型和实例的库,可以帮助提供建议。

重新思考数据库

更新

根据要求,这是产生错误的完整代码清单。

main = do
  pool <- createPool (connect "localhost" 28015 Nothing) close 1 300 5
  let rdb q = withResource pool (\h -> run' (use h $ db "test") q)
  scotty 3000 $ basal rdb

basal :: Expr q => (q -> IO [JSON]) -> ScottyM ()
basal r = get "/json" $ showJson r

showJson :: Expr q => (q -> IO [JSON]) -> ActionM ()
showJson r = do
  j <- lift $ r $ table "mytable"
  text $ T.pack $ show j

这是完整的错误列表

Main.hs:19:17:
    No instance for (Expr q0) arising from a use of `basal'
    The type variable `q0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Expr () -- Defined in `Database.RethinkDB.ReQL'
      instance (Expr a, Expr b) => Expr (a, b)
        -- Defined in `Database.RethinkDB.ReQL'
      instance (Expr a, Expr b, Expr c) => Expr (a, b, c)
        -- Defined in `Database.RethinkDB.ReQL'
      ...plus 24 others
    In the second argument of `($)', namely `basal rdb'
    In a stmt of a 'do' block: scotty 3000 $ basal rdb
    In the expression:
      do { pool <- createPool
                     (connect "localhost" 28015 Nothing) close 1 300 5;
           let rdb q = withResource pool (\ h -> ...);
           scotty 3000 $ basal rdb }

Main.hs:26:19:
    Could not deduce (q ~ Table)
    from the context (Expr q)
      bound by the type signature for
                 showJson :: Expr q => (q -> IO [JSON]) -> ActionM ()
      at Main.hs:24:13-52
      `q' is a rigid type variable bound by
          the type signature for
            showJson :: Expr q => (q -> IO [JSON]) -> ActionM ()
          at Main.hs:24:13
    In the return type of a call of `table'
    In the second argument of `($)', namely `table "mytable"'
    In the second argument of `($)', namely `r $ table "mytable"'

谢谢

4

1 回答 1

2

阅读错误消息似乎第一个问题是您指定的类型showJson是错误的。

As ris 直接应用于tablewhich istable :: String -> Table它的类型不是

r :: Expr q => q -> IO [JSON]

但相反

r :: Table -> IO [JSON]

或(使用RankNTypes

r :: forall q . Expr q => q -> IO [JSON]

第一个更简单,更直接,而第二个可能更接近您的预期含义-可以将其理解为“fromJson接受它要求的输入,仅使用Expr其参数上的接口”而不是“fromJson接受发生的任何类型的输入使用Expr实例化类型作为其参数”。例如,使用您给出的类型

fromJson (undefined :: Query -> IO [JSON])

也会统一......但显然这就是现在r在函数体中使用的方式。

(特别是,它与参数 的积极性有关q。由于这个函数的编写方式,q它更像是一个输出参数而不是输入参数。事实上,该函数创建一个Table(with table) 而不是要求一个。因此,您编写的参数意味着我们有一个函数Expr q => Table -> q。)

现在,这种类型的特殊性也向上传播,导致basal具有类型

basal :: (Table -> IO [JSON]) -> ScottyM ()

或者

basal :: (forall q . Expr q => q -> IO [JSON]) -> ScottyM ()

从而导致您的Cannot deduce (q ~ Table)错误。


在这一点上,我不确定为什么声明一个显式类型rdb会导致问题,但可能是清除这个会阻止问题在那里发生。通常,一旦你已经破坏了类型系统,就很难预测它在其他位置的行为。

于 2014-04-01T13:29:31.430 回答