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

5

使用这段代码

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


1
据我所知,目前绝对没有办法在导入/导出列表中引用实例。你很遗憾。请参见:https://dev59.com/sGoy5IYBdhLWcg3wKq-s - Bakuriu
1个回答

4

Haskell 2010 report中第5.4节规定:

在导入或导出列表中不能显式命名实例声明。模块内所有实例都总是被导出,任何导入都会从导入的模块中带入所有实例。因此,只有通过一系列import声明导向包含实例声明的模块时,实例声明才在作用域内。

所以,标准规定不能做你试图做的事情。

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

请参见明确导入实例的示例。

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

实际上,GHC已经不完全遵守标准,允许存在格式错误的程序。它不会全局检查实例声明,而只是在需要时检查一个实例是否在作用域内。请参见this answer


在您的情况下,您可能应该在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 即可。

如果实例始终被导入,隐藏模块(如果可能的话)也不会隐藏它们。感谢“newtype”的建议! - user2556165

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接