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?

+3


source to share


1 answer


{-# 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.

+6


source







All Articles