2016-08-05 37 views
0

我有這種類型的同義詞:重新定義實例要麼與特定類型

type ParseResult a = Either [CompilerError] (a, [CompilerWarning]) 

CompilerErrorCompilerWarning是數據類型。

現在我知道,無論是有實例FunctorApplicativeFunctor實例上的元組(a,[CompilerWarning])適用fmap,我要重新定義實例對於這種類型的同義詞,這樣fmap適用於a不整元組,同去爲Applicative

如果我使用newtype,我將不得不把ParseResult放在各處,我已經寫了很多代碼。

我知道我需要TypeSynonymInstances但我this問題面臨着同樣的問題,從我想我需要定義我喜歡的類型同義詞這樣的問題:

type ParseResult = ... 

我需要填寫...,我不知道如何使* -> *Eithertuple,我試圖Either [CompilerError] ((,) [CompilerWarning]),但這有兩個問題:第一個CompilerWarning是第一個元素,我需要它是第二個(所以我沒有改變很多代碼),第二我得到這個消息:

•期待一個更參數 '(,)[CompilerWarning]' 預期一個類型,但 '(,)[CompilerWarning]' 具有一種 '* - > *' •在任一「的第二個參數」,即 '(,)[CompilerWarning]' 在類型 '爲[CompilerError]((,)[CompilerWarning])' 在類型聲明 'ParseResult'

什麼是最好的,最昂貴的解決這個問題?

+2

製作功能'ParseResult'一個'newtype',而不是一個類型別名。這樣,你可以在它上定義自己的實例,而不會與已經爲'Either'定義的實例衝突。 –

+0

如果您可以將數據類型更改爲'[CompileError]([CompilerWarning],a)',您可以簡單地使用'fmap'兩次:'(fmap。fmap)(+1)(Right([],1)) '評估爲'正確([],2)'。 – chepner

+0

我真的不明白TypeSynonymInstances如何引起如此多的混淆。它所做的一切就是用實例擴展替換實例頭中的類型同義詞。你可以自己做,所以你永遠不需要TypeSynonymInstances。 –

回答

4

您可以利用Either(,)雙功能器,而不僅僅是仿函數。這意味着使用second . first而不是fmap將函數應用於類型a的值。

> import Data.Bifunctor 
> (second . first) (+1) (Right (1, [])) 
Right (2, []) 
> (second . first) (+1) (Left ["oops"]) 
Left ["oops"] 

first f (x, y)相當於(f x, y)

second f (Right x)相當於Right f x,而second f (Left y)相當於Left y

把它們放在一起,你可以看到,

(second . first) (+1) (Right (1, [])) == second (first (+1)) (Right (1, [])) 
             == Right $ first (+1) (1, []) 
             == Right ((+1) 1, []) 
             == Right (2, []) 

如果你有Left代替,沒有任何反應。

(second . first) (+1) (Left ["oops"]) == second (first (+1)) (Left ["oops"]) 
             == Left ["oops"] 

由於fmap相同secondEither,這意味着你仍然可以使用fmap。您只需在使用前用first包裝該功能。

(second . first) f == second (first f) 
        == fmap (first f) 

因此

> import Data.Bifunctor 
> fmap (first (+1)) (Right (1, [])) 
Right (2, []) 
> fmap (first (+1)) (Left ["oops"]) 
Left ["oops"] 
5

不能重新定義現有的情況下(這將是,如果你能可怕)。

的選項有:

  • ParseResult一個真正的類型,或者使用newtype或類似

    data ParseResult a = Failure [CompilerError] | Success a [CompilerWarning] 
    

    ,並定義實例吧。

  • 不要在所有類型類打擾,只是這樣定義

    mapParseResult :: (a -> b) -> ParseResult a -> ParseResult b