Избегание мономорфизма в привязках let без аннотации типа
У меня есть некоторый код, использующий типы для устранения неоднозначности экземпляров (настоящий код использует синглтоны GHC.TypeLits для тегов типов, но я не думаю, что это уместно), и я хотел бы использовать привязку let, чтобы избежать дублирования на уровне текста; к сожалению, это мономорфизирует результат.
Ниже приведен пример проблемы:
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)
Приведенный выше код не компилируется (но, конечно, если я наберу аннотировать quux
чтобы быть полиморфным, все работает нормально), справедливо жаловался, что не может соответствовать Int
с Char
, Есть ли какой-нибудь способ, которым я мог бы добиться успеха компиляции без аннотирования типа и без повторения bar undefined
на каждом сайте использования?
1 ответ
{-# LANGUAGE NoMonomorphismRestriction #-}
Или если вы хотите что-то менее глобальное
let quux () = bar undefined in
print (idInt (quux ()) >> print (idChar (quux ()))
Причина последней работы состоит в том, что привязки мономорфизируются только тогда, когда они не имеют аргументов слева от знака равенства.
let foo = \x y -> x + y -- :: Integer -> Integer -> Integer
let bar x y = x + y -- :: (Num a) => a -> a -> a
Так чтобы получить quux
чтобы не мономорфизировать, вы должны дать ему аргумент слева от знака равенства. Если quux
это не значение, а функция, вы можете просто расширить ее, чтобы получить тот же эффект:
let quux x = bar undefined x in ...
Для первого не беспокойтесь о производительности - если вы всегда называете это как quux ()
затем он будет встроен и сгенерирует тот же код, что и версия с явной сигнатурой типа.