Haskell 在没有类型注释的let绑定中避免单态

Haskell 在没有类型注释的let绑定中避免单态,haskell,polymorphism,let,monomorphism,Haskell,Polymorphism,Let,Monomorphism,我有一些使用类型来消除实例歧义的代码(真正的代码是使用GHC.TypeLits singleton作为类型标记,但我不认为这有什么关系),我想使用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

我有一些使用类型来消除实例歧义的代码(真正的代码是使用GHC.TypeLits singleton作为类型标记,但我不认为这有什么关系),我想使用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)
上面的代码没有编译(但是,当然,如果我键入annotate
qux
作为多态,一切都可以正常工作),正确地抱怨它不能将
Int
Char
匹配。有没有什么方法可以使编译成功,而不需要在每个使用站点上进行类型注释和重复
bar undefined

{-# 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
因此,要使qux
not monomorphize,必须在等号的左边给它一个参数。如果
qux
不是一个值而是一个函数,您可以简单地进行扩展以获得相同的效果:

let quux x = bar undefined x in ...

对于前者,不要担心性能——如果您总是将其称为
qux()
,那么它将被内联并生成与具有显式类型签名的版本相同的代码。

我认为
NoMonomorphismRestriction
仅应用于顶级绑定。呵呵。我对运行代码感到惊讶。