Haskell:GHC不能推断出类型.由类型签名错误绑定的刚性类型变量

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

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

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(ReQL和Table都是其实例)将停止并显示上述错误.将签名全部放在一起工作正常.

那么我该如何解决这个问题呢?丢弃签名感觉不对.

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

Rethink DB

UPDATE

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

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"'

谢谢

最佳答案 阅读错误消息似乎第一个问题是你为showJson指定的类型是错误的.

因为r直接应用于table :: String – >这个表.表的类型不是

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更像输出参数而不是输入参数.实际上,函数创建一个表(带表)而不是你写的这个论点意味着我们有一个函数Expr q => Table – > q.)

现在,这种类型的特异性向上传递也导致基础具有类型

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

要么

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

从而导致你无法推断(q~Table)错误.

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

点赞