2015-08-29 85 views
3

有了这个代码如何导入模块隐藏子模块或实例?

import Control.Monad 
import Control.Applicative 
import Control.Monad.State 

class DefVal a where 
    defVal :: a 

instance (DefVal a) => Alternative (Either a) where 
    empty = Left defVal 
    (Left _) <|> x = x 
    x  <|> _ = x 

instance (DefVal a) => MonadPlus (Either a) where 
    mzero = empty 
    mplus = (<|>) 

newtype ErrString = ErrString { toString :: String } 
    deriving Show 

instance DefVal ErrString where 
    defVal = ErrString "Default Error String" 

遇到错误:

Duplicate instance declarations: 
    instance DefVal a => Alternative (Either a) 
    -- Defined at main.hs:10:10 
    instance Control.Monad.Trans.Error.Error e => 
      Alternative (Either e) 
    -- Defined in ‘Control.Monad.Trans.Error’ 

Duplicate instance declarations: 
    instance DefVal a => MonadPlus (Either a) 
    -- Defined at main.hs:15:10 
    instance Control.Monad.Trans.Error.Error e => MonadPlus (Either e) 
    -- Defined in ‘Control.Monad.Trans.Error’ 

错误,如果我删除Control.Monad.State其中进口Control.Monad.Trans.Error消失。

如何导入Control.Monad.State隐藏Control.Monad.Trans.Error或隐藏的AlternativeEither实例,并从那里MonadPlus

我用GHC-7.10.2

+1

AFAIK目前绝对没有办法引用导入/导出列表中的实例。你运气不好。请参阅:http://stackoverflow.com/questions/8728596/explicitly-import-instances – Bakuriu

回答

4

Haskell 2010 report,第5.4节说:

实例声明不能被明确命名的进口或出口 名单。模块范围内的所有实例都是总是已导出,并且 任何导入都会从导入的模块中导入所有实例。因此,一个 实例声明在范围内,当且仅当声明的一个连锁导致包含实例声明的模块。

所以这是由标准决定了你不能做你正在尝试做的。

原因是,通过允许这将有可能创建混合不同实例产生不正确结果的程序。例如

参见Explicitly import instances

有一些扩展名(例如GeneralizedNewtypeDeriving,请参阅Breaking Data.Set integrity without GeneralizedNewtypeDeriving),即使没有导出/导入列表中的实例也允许混合实例。

事实上,GHC已经不能100%地坚持这个标准,并允许格式错误的程序。它不全局检查实例声明,但只有一个实例在需要的范围内。见this answer


你的情况,你应该使用一些newtype各地Either避免混合实例:

newtype MyEither e a = MyEither {runMyEither :: Either e a} 


instance (DefVal a) => Alternative (MyEither a) where 
    empty = MyEither (Left defVal) 
    (MyEither (Left _)) <|> x = x 
    x     <|> _ = x 

instance (DefVal a) => MonadPlus (MyEither a) where 
    mzero = empty 
    mplus = (<|>) 

谁想值x ::Either a b作为x :: MyEither a b 可以简单地做MyEither x,然后用runMyEither上结果。

+0

如果实例总是导入,隐藏模块(如果可能的话)不会隐藏它们。感谢'newtype'的建议! – wowofbob