I have a bit of code that would be more cleanly written if I could treat Monads as Nums (where applicable, of course). Easily enough done:
{-# LANGUAGE FlexibleInstances #-}
import Control.Monad (liftM, liftM2)
import Data.Char (digitToInt)
instance (Monad m, Num a) => Num (m a) where
(+) = liftM2 (+)
(-) = liftM2 (-)
(*) = liftM2 (*)
abs = liftM abs
signum = liftM signum
fromInteger = return . fromInteger
square :: (Monad m, Num a) => m a -> m a
square x = x * x
-- Prints "Just 9", as expected
main = putStrLn $ show $ square $ Just 3
But when I add the following function to the file …
digitToNum :: (Num a) => Char -> a
digitToNum = fromIntegral . digitToInt
… I receive the following error:
monadNumTest.hs:15:14:
Overlapping instances for Num (m a)
arising from a use of `*'
Matching instances:
instance (Monad m, Num a) => Num (m a)
-- Defined at monadNumTest.hs:6:10
instance Integral a => Num (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
(The choice depends on the instantiation of `m, a'
To pick the first instance above, use -XIncoherentInstances
when compiling the other instance declarations)
In the expression: x * x
In an equation for `square': square x = x * x
This doesn’t make sense to me, because (1) digitToNum is never called and (2) Ratio isn’t a Monad. So I’m unsure how or why that’s a problem. Any tips around this would be appreciated.
This is GHC 7.4.2, using the Haskell Platform 2012.4.0.0.
The key issue at play here is a principle in haskell that writing additional instances should not change the operation of existing code. This increases the robustness of haskell code, as code won’t break or have different behaviour if a module it depends on adds a new instance.
For this reason, when choosing possible instances to use for a type, Haskell does not consider the context of the instances. For example, when matching checking if a type will match the instance
instance (Monad m, Num a) => Num (m a)for the classNum, it will only check if it can matchm a. This is because any type may later be made an instance of a class, and if instance choice used context information adding that instance would change the operation of existing programs.For example, there is nothing stopping the following code being added either in your module, or in a module you depend on:
Sure, such an instance is useless, but haskell has no way of judging that. It also is possible that there is a useful definition of
MonadforRatio(I haven’t looked into that).In summary, what you are trying to do isn’t a good idea. You can stop these limitations using
OverlappingInstancesandIncoherentInstances, however for the reasons described above these flags are not recommended for mainstream use by most haskell programmers.