具有 Haskeline 自动完成功能的模糊类型变量

Ambiguous type variable with Haskeline auto-completion

我正在尝试使用 Haskeline 实现自动完成功能:

import System.Console.Haskeline
import System.Console.Haskeline.IO
import Data.List

mySettings :: Settings IO
mySettings = defaultSettings {
                                historyFile = Just "myhist"
                              , complete = completeWord Nothing " \t" $ return . search
                              }

keywords :: [String]
keywords = ["Point","Line","Circle","Sphere"]

search :: String -> [Completion]
search str = map simpleCompletion $ filter (str `isPrefixOf`) keywords

main :: IO ()
main = do
        inputLine <- initializeInput mySettings
        putStrLn "done"

但是这个 GHC 错误让我有点失望:

Ambiguous type variable `t0' in the constraint:
  (Control.Monad.IO.Class.MonadIO t0)
    arising from a use of `defaultSettings'
Probable fix: add a type signature that fixes these type variable(s)

我为每个函数都设置了类型,但并没有解决问题。

您知道这种类型歧义从何而来以及如何消除它吗?

快速修复:

mySettings :: Settings IO
mySettings = (defaultSettings :: Settings IO)
  {  historyFile = Just "myhist"
   , complete    = completeWord Nothing " \t" $ return . search } 

这个问题是一个非常罕见的极端案例,所以上面的解决方案看起来很武断或难以理解也就不足为奇了。尽管如此,我还是试着解释一下。

defaultSettings 的类型为 MonadIO m => Settings m。它是一个 多态值 ,此类值通常会导致类型推断出现问题。通常,如果 GHC 可以从上下文中推断出多态参数,我们只能对多态值进行计算(模式匹配、场投影等)。 Settings m 可能具有完全不同的内容,具体取决于属于 m 的确切 m 和确切类型 class 方法。

现在,Settings 的问题是 m 参数仅出现在类型为 CompletionFunc mcomplete 字段中。但在我们的示例中,我们 忽略 旧的 complete 字段,并用新字段替换它。因此,据 GHC 所知,旧的 complete 字段可能是 any 类型。由于旧的 complete 字段是我们可能获得有关 defaultSettingsm 参数的信息的唯一来源,并且我们让它完全不受约束,GHC 无法推断出m 是一个 MonadIO

如果我们添加 (defaultSettings :: Settings IO),那么旧的 m 参数将实例化为 IO,并且不再有问题。注意 new m 参数与旧的 m 参数完全无关,因为我们只是忽略了旧的 complete 字段并将其替换为新功能。新的m参数被顶层mySettings :: Settings IO注解确定为IO

事实上,我们可以用任何 MonadIO 类型实例化 defaultSettings,结果都是一样的。同样,这是因为我们忽略了 complete 的旧值。

Settings的类型有点多态。请注意,haskeline 作者意识到了这个可能的问题,并提供了一个 a setComplete function 来避免这个特殊问题。正如其他答案所示,手动指定类型也是一种选择。