重新定义具有特定类型的Either的实例

时间:2016-08-05 17:54:48

标签: haskell

我有这种类型的同义词:

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

CompilerErrorCompilerWarning是数据类型。

现在我知道有FunctorApplicative的实例,但Functor的实例在元组fmap上应用(a,[CompilerWarning]),我想重新定义此类型同义词的实例,以便fmap适用于a而不是整个元组,Applicative也是如此。

如果我使用newtype,我必须将ParseResult放在所有地方,我已经编写了很多代码。

我知道我需要TypeSynonymInstances但是我在this问题中面临同样的问题,我认为我需要定义类型同义词这样的问题:

type ParseResult = ...

我需要填写...,我不知道如何与* -> *Either建立良好的tuple,我试过{{1}但是这有两个问题:第一个Either [CompilerError] ((,) [CompilerWarning])是第一个元素,我需要它成为第二个元素(所以我不需要更改很多代码),第二个我得到这个消息:

  

•期待“(,)[CompilerWarning]”的另一个参数         期望一个类型,但'(,)[CompilerWarning]'有种'* - > *”       •在'Either'的第二个参数中,即           '(,)[CompilerWarning]'         在类型'Either [CompilerError]((,)[CompilerWarning])'         在'ParseResult'的类型声明中

这个问题的最佳,最便宜的解决方案是什么?

2 个答案:

答案 0 :(得分:5)

您无法重新定义现有实例(如果可以的话,这将是非常糟糕的。)

您的选择是:

  • 使用ParseResult

    之类的内容newtype成为真实类型
    data ParseResult a = Failure [CompilerError] | Success a [CompilerWarning]
    

    并为其定义实例。

  • 根本不打扰类型类,只需定义类似

    的函数
    mapParseResult :: (a -> b) -> ParseResult a -> ParseResult b
    

答案 1 :(得分:4)

您可以利用Either(,) bifunctors ,而不仅仅是仿函数。这意味着使用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"]

由于fmapsecond的{​​{1}}相同,这意味着您仍然可以使用Either。在使用之前,您只需要使用fmap包装该函数。

first

因此

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