Search code examples
haskellfunctorinstances

What makes fmap work here without an explicit method declaration?


One of the exercises in Real World Haskell, ch. 24, asks to implement a strictness wrapper around Control.Concurrent.MVar. I am doing this, as suggested in the book, by using a newtype MVarS wrapper to ensure that evaluate is applied to whatever arguments get passed to functions such as newMVar and putMVar.

Now, one of the functions to wrap is mkWeakMVar, whose type is MVar a -> IO () -> IO (Weak (MVar a)). Assuming that my MVarS builder functions implement strictness, I reasoned that for mkWeakMVar it would suffice to put MVarS in place of its MVars. So I wrote the following:

import           Control.Concurrent.MVar
import           System.Mem.Weak

instance Functor Weak

newtype MVarS a = MVarS (MVar a)

mkWeakMVarS :: MVarS a -> IO () -> IO (Weak (MVarS a))
mkWeakMVarS (MVarS mv) x = (fmap . fmap) MVarS (mkWeakMVar mv x)

This appears to work, even though GHCi warns that there is no explicit method declaration of fmap for Functor Weak. But it leaves me intrigued. What makes fmap work in this case?


Solution

  • While the above code will typecheck, GHC will crash when attempting to evaluate a value which requires calling upon the missing fmap implementation. It'll look a bit like:

    *** Exception: /Users/tel/tmp/SO.hs:31:10-18: 
        No instance nor default method for class operation GHC.Base.fmap
    

    Since this is a rather catastrophic and entirely avoidable runtime error it ought to serve as a testament to the importance of -Wall.