Search code examples
haskellghci

GHCi not infering instance for some IO action


Running the following in GHCi throws an error on stuff' 4 but not on stuff 4. Why ?

Is there a way to fix it ? I would like to use MkStuff' and not MkStuff.

ghci> 
:{
class MkStuff s m | m -> s where
  stuff:: s -> m ()

instance MkStuff Int IO where
  stuff = print

class MkStuff' s m | m -> s where
  stuff' :: s -> m

instance MkStuff' Int (IO ()) where
  stuff' = print
:}

ghci> stuff 4
4
ghci> stuff' 4

<interactive>:16:1: error: [GHC-39999]
    • No instance for ‘MkStuff' Integer ()’ arising from a use of ‘it’
    • In the first argument of ‘print’, namely ‘it’   
      In a stmt of an interactive GHCi command: print it

Solution

  • While I am not completely sure, I believe that this is what happens.

    The GHCi has some magic inside. After entering an expression, GHCi roughly attempts the following:

    1. Try to infer e :: IO a for some a. If possible:

      a. Check if a is a Show type. If so, run action e and then print its resulting value of type a (unless a~()).

      b. Otherwise, run action e and then print its resulting a value.

    2. Otherwise, try to infer e :: a for some a of class Show. If possible, print the string obtained by show e.

    3. Otherwise, print a type error.

    In your first case, when inferring stuff 4 :: IO a GHCi can see that stuff 4 :: m (), hence m ~ IO. That can be used to commit to your instance, and everything proceeds smoothly.

    In your second case, when inferring stuff' 4 :: IO a GHCi can see that stuff' 4 :: m, hence m ~ IO a. This is not enough to commit to the instance because the instance is about IO (), and a has not (yet) been inferred as (). If another instance instance MkStuff' Char (IO Bool) where ... existed it could provide another candidate, so we can not commit.

    Failing in step 1., we turn to step 2. but no concrete a can be found. Defaulting rules try a ~ (), hence your weird error No instance for ‘MkStuff' Integer ()’. Alas, this defaulting occurs too late in the process to allow committing to the IO instance.

    Try making the instance head more general:

    instance a ~ () => MkStuff' Int (IO a) where
      stuff' = print
    

    This correctly allows GHCi to commit to the instance. Note that you will not be able to add further instances involving IO in this way.