如何导入模块隐藏子模块或实例



使用下面的代码

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.Trans.ErrorControl.Monad.State,错误将消失。

我如何导入Control.Monad.State隐藏Control.Monad.Trans.Error或隐藏AlternativeMonadPlusEither实例?

我用GHC-7.10.2

Haskell 2010 report, section 5.4说:

实例声明不能在导入或导出时显式命名列表。模块范围内的所有实例都是,总是导出和任何导入都会从导入模块中导入所有实例。因此,一个实例声明在作用域中当且仅当importDeclarations指向包含实例声明的模块。

所以是由标准决定的,你不能做你想做的。

原因是允许这样做就有可能创建混合不同实例的程序,从而产生不正确的结果。

参见显式导入实例。

有一些扩展(例如GeneralizedNewtypeDeriving,参见破坏数据。设置完整性,不带GeneralizedNewtypeDeriving),允许混合实例,即使在导出/导入列表中没有实例。

事实上,GHC在这方面并没有100%遵守标准,并且允许畸形程序。它不全局地检查实例声明,而只检查只有一个实例在需要它的范围内。请看这个答案


在你的情况下,你可能应该在Either周围使用一些newtype,以避免混合实例:

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的结果

最新更新