I've got some code using types to disambiguate instances (the real code is using GHC.TypeLits singletons for type tags, but I don't think that's germane) and I'd like to use a let binding to avoid text-level duplication; unfortunately, this monomorphizes the result.
What follows is an example of the problem:
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, everything works fine), rightly complaining that it couldn't match Int with Char. Is there any way I could get compilation to succeed without type-annotating and without repeating bar undefined at each use site?
{-# LANGUAGE NoMonomorphismRestriction #-}
Or if you want something less global
let quux () = bar undefined in
print (idInt (quux ()) >> print (idChar (quux ()))
The reason the latter works is that bindings are only monomorphised when they have no arguments to the left of the equals sign.
let foo = \x y -> x + y -- :: Integer -> Integer -> Integer
let bar x y = x + y -- :: (Num a) => a -> a -> a
So to get quux to not monomorphize, you have to give it an argument to the left of the equals sign. If quux is not a value but a function, you can simply eta expand 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 as quux (), then it will be inlined and generate the same code as the version with an explicit type signature.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With