Avoiding monomorphism in let bindings without type annotation
I have code that uses types to disambiguate instances (the real code uses GHC.TypeLits singlets for type tags, but I don't think this is related) and I would like to use let binding to avoid text-level duplication; unfortunately this monomorphoses the result.
An example of a problem follows:
class Foo a where
foo :: a
instance Foo Int where
foo = 0
instance Foo Char where
foo = 'a'
data Bar a = Bar String
deriving (Show)
bar :: forall a. (Show a, Foo a) => a -> Bar a
bar _ = Bar $ show (foo :: a)
idInt :: Bar Int -> Bar Int
idInt = id
idChar :: Bar Char -> Bar Char
idChar = id
main = let quux = bar undefined in
print (idInt quux) >> print (idChar quux)
The above code doesn't compile (but of course if I type annotate quux
to be polymorphic it works fine), rightly complaining that it can't match Int
with Char
. Is there any way to achieve compilation to succeed without type annotation and without repetition bar undefined
on every site you use?
source to share
{-# LANGUAGE NoMonomorphismRestriction #-}
Or if you want something less global
let quux () = bar undefined in
print (idInt (quux ()) >> print (idChar (quux ()))
The reason for the latter is that bindings are only monomorphic when they have no arguments to the left of the equal sign.
let foo = \x y -> x + y -- :: Integer -> Integer -> Integer
let bar x y = x + y -- :: (Num a) => a -> a -> a
So, in order quux
not to monomorphize, you have to give it an argument to the left of the equal sign. If quux
it is not a value but a function, you can simply increase it to get the same effect:
let quux x = bar undefined x in ...
For the former, don't worry about performance - if you always call it quux ()
then it will be inlined and generate the same code as the explicitly type signed version.
source to share