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

wow*_*bob 4 import haskell module

有了这个代码

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"
Run Code Online (Sandbox Code Playgroud)

我收到错误消息:

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’
Run Code Online (Sandbox Code Playgroud)

如果我删除Control.Monad.State哪些导入,错误消失Control.Monad.Trans.Error

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

我用 GHC-7.10.2

Bak*_*riu 5

Haskell 2010年报告第5.4节说:

实例声明不能在导入或导出列表上明确命名。始终导出模块范围内的所有实例,并且任何导入都会从导入的模块中引入所有实例。因此,当且仅当import 声明链导致包含该实例声明的模块时,实例声明才在范围内。

因此,由标准决定,您不能做自己想做的事情。

原因是通过允许这样做,可以创建混合了不同实例并产生错误结果的程序。

例如,请参阅显式导入实例

有一些扩展(例如GeneralizedNewtypeDeriving,请参见Breaking Data.Set完整性而不具有GeneralizedNewtypeDeriving),即使在导出/导入列表中没有实例的情况下,也可以混合使用实例。

实际上,GHC尚未对此标准100%遵守,并且允许格式错误的程序。它不会全局检查实例声明,而只在需要它的范围内检查一个实例。看到这个答案


在你的情况你应该使用一些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 = (<|>)
Run Code Online (Sandbox Code Playgroud)

想要值x ::Either a b发挥作用的人x :: MyEither a b 可以简单地做MyEither x然后使用runMyEither结果。