I'm trying to implement an autocompletion function with 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"
but I am a bit disappointed by this GHC error :
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)
I set the type for each function but it didn't solve the problem.
Do you have an idea where does this type ambiguity come from and how to remove it?
A quick fix:
The problem is a really rare corner case, so it's no wonder that the above solution may seem arbitrary or inscrutable. I try to explain it nevertheless.
defaultSettings
has typeMonadIO m => Settings m
. It's a polymorphic value, and such values often cause hiccups in the type inference. Generally, we can only do computation (pattern matching, field projections, etc.) on polymorphic values if GHC can infer the polymorphic parameter from the context.Settings m
might have completely different content depending on the exactm
and the exact type class methods that belong tom
.Now, the issue with
Settings
is that them
parameter is only present in thecomplete
field, which has typeCompletionFunc m
. But in our example we ignore the oldcomplete
field, and just replace it with a new field. Therefore, as far as GHC knows, the oldcomplete
field could have been of any type whatsoever. And since the oldcomplete
field is the only source from which we could possibly gain information about them
parameter ofdefaultSettings
, and we left it fully unconstrained, GHC can't infer that thatm
is aMonadIO
.If we add
(defaultSettings :: Settings IO)
, then the oldm
parameter is instantiated toIO
and there's no issue anymore. Note that the newm
parameter is completely unrelated to the oldm
parameter, because we just ignored the oldcomplete
field and replaced it with the new function. The newm
parameter is determined to beIO
by the top-levelmySettings :: Settings IO
annotation.In fact, we could instantiate
defaultSettings
with anyMonadIO
type, and the result would be the same. Again, this is because we ignore the old value ofcomplete
.