2015-08-29 127 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