I have a bit of code that would be more cleanly written if I could treat Monad
s as Num
s (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 class Num
, it will only check if it can match m 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:
instance Monad Ratio where
return = undefined
(>>=) = undefined
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 Monad
for Ratio
(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 OverlappingInstances
and IncoherentInstances
, however for the reasons described above these flags are not recommended for mainstream use by most haskell programmers.