我有这种类型的同义词:
type ParseResult a = Either [CompilerError] (a, [CompilerWarning])
CompilerError
和CompilerWarning
是数据类型。
现在我知道有Functor
和Applicative
的实例,但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'的类型声明中
这个问题的最佳,最便宜的解决方案是什么?
答案 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"]
由于fmap
与second
的{{1}}相同,这意味着您仍然可以使用Either
。在使用之前,您只需要使用fmap
包装该函数。
first
因此
(second . first) f == second (first f)
== fmap (first f)