2016-08-05 38 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