3

我有一些使用类型来消除实例歧义的代码(真正的代码是使用 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)

上面的代码不能编译(但是,当然,如果我输入 annotatequux是多态的,一切正常),正确地抱怨它不能IntChar. 有没有什么方法可以让编译成功而无需类型注释并且无需bar undefined在每个使用站点重复?

4

1 回答 1

6
{-# 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不是一个值而是一个函数,您可以简单地进行 eta 扩展以获得相同的效果:

let quux x = bar undefined x in ...

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

于 2013-02-02T01:14:51.677 回答