Ambiguous type variable with Haskeline auto-completion
Asked Answered
C

2

5

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?

Coconut answered 1/4, 2015 at 12:28 Comment(1)
It is indeed weird. I think that it has something to do with the fact that the record updates needs to disambiguate the type before being able to check if the updates are correct or not. Here you can find a SSCCE for a more general example.Dora
O
6

A quick fix:

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

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 type MonadIO 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 exact m and the exact type class methods that belong to m.

Now, the issue with Settings is that the m parameter is only present in the complete field, which has type CompletionFunc m. But in our example we ignore the old complete field, and just replace it with a new field. Therefore, as far as GHC knows, the old complete field could have been of any type whatsoever. And since the old complete field is the only source from which we could possibly gain information about the m parameter of defaultSettings, and we left it fully unconstrained, GHC can't infer that that m is a MonadIO.

If we add (defaultSettings :: Settings IO), then the old m parameter is instantiated to IO and there's no issue anymore. Note that the new m parameter is completely unrelated to the old m parameter, because we just ignored the old complete field and replaced it with the new function. The new m parameter is determined to be IO by the top-level mySettings :: Settings IO annotation.

In fact, we could instantiate defaultSettings with any MonadIO type, and the result would be the same. Again, this is because we ignore the old value of complete.

Original answered 1/4, 2015 at 13:43 Comment(0)
P
3

The type of Settings is a bit too much polymorphic. Note that haskeline authors were aware of this possible problem and provided a a setComplete function to avoid this particular problem. Specifying the type manually is also an option as other answers show.

Parrott answered 1/4, 2015 at 15:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.