Haskell 使用特定类型重新定义其中一个的实例
我有这样的同义词:Haskell 使用特定类型重新定义其中一个的实例,haskell,Haskell,我有这样的同义词: type ParseResult a = Either [CompilerError] (a, [CompilerWarning]) 其中编译器错误和编译器警告是数据类型 现在我知道有Functor和Applicative的实例,但是Functor的实例在元组(a,[CompilerWarning])上应用fmap,我想重新定义这个类型同义词的实例,以便fmap应用于a而不是整个元组,这同样适用于Applicative 如果我使用newtype,我将不得不把ParseRes
type ParseResult a = Either [CompilerError] (a, [CompilerWarning])
其中编译器错误
和编译器警告
是数据类型
现在我知道有Functor
和Applicative
的实例,但是Functor
的实例在元组(a,[CompilerWarning])
上应用fmap
,我想重新定义这个类型同义词的实例,以便fmap
应用于a
而不是整个元组,这同样适用于Applicative
如果我使用newtype
,我将不得不把ParseResult
放得到处都是,我已经写了很多代码了
我知道我需要类型同义词实例
,但我面临着同样的问题,从这个问题上我认为我需要这样定义我的类型同义词:
type ParseResult = ...
我需要用或者和元组来填充。
,我不知道如何使类的右侧*->*
,我尝试了或者[CompilerError](,)[CompilerWarning])
,但这有两个问题:第一编译器警告是第一个元素,我需要它是第二个元素(这样我就不必更改很多代码),第二,我得到了以下信息:
•需要多个参数“(,)[CompilerWarning]”
应为类型,但“(,)[CompilerWarning]”具有种类“*->*”
•在“任一”的第二个参数中,即
“(,)[CompilerWarning]”
在类型“或[CompilerError](,)[CompilerWarning])中
在“ParseResult”的类型声明中
这个问题的最佳、最便宜的解决方案是什么?您可以利用或者或者(,)
都是双函数,而不仅仅是函子。这意味着使用第二个。第一个
而不是fmap
将函数应用于a
类型的值
> import Data.Bifunctor
> (second . first) (+1) (Right (1, []))
Right (2, [])
> (second . first) (+1) (Left ["oops"])
Left ["oops"]
第一个f(x,y)
相当于(fx,y)
第二个f(右x)
相当于右f x
,而第二个f(左y)
相当于左y
把它们放在一起,你可以看到
(second . first) (+1) (Right (1, [])) == second (first (+1)) (Right (1, []))
== Right $ first (+1) (1, [])
== Right ((+1) 1, [])
== Right (2, [])
如果您有一个左
,则什么也不会发生
(second . first) (+1) (Left ["oops"]) == second (first (+1)) (Left ["oops"])
== Left ["oops"]
由于fmap
与或的second
相同,这意味着您仍然可以使用fmap
。您只需先用将函数包装起来,然后再使用它
(second . first) f == second (first f)
== fmap (first f)
因此
您无法重新定义现有实例(如果可以的话,那将非常糟糕)
你的选择是:
- 使用
newtype
或类似方法,将ParseResult
设置为实类型
data ParseResult a = Failure [CompilerError] | Success a [CompilerWarning]
并为其定义实例
- 根本不用担心类型类,只需定义如下函数
mapParseResult :: (a -> b) -> ParseResult a -> ParseResult b
MakeParseResult
anewtype
而不是类型别名。这样,您可以在其上定义自己的实例,而不会与已为或定义的实例冲突。如果您可以将数据类型更改为或[CompilerError]([CompilerWarning],a)
,您可以简单地fmap
两次:(fmap.fmap)(+1)(右([],1))
计算为右([],2)
。我真的不明白TypeSynonymInstances是如何引起如此多的混乱的。它所做的只是用扩展替换实例头中的类型同义词。你也可以自己做,所以你永远不需要TypeSynonymInstances。除了重新定义的罪恶,绝对没有办法定义函子
实例e对于<代码>要么<代码>,要么做你想要的。我敦促你切换到一个新类型,或者甚至是一个没有<代码>的数据类型< < /C> >,并考虑你想在哪里提取抽象线,使你的包有意义,并尽可能多地重用现有代码。你可能喜欢现有的<代码>文字[CuffeleRebug ](除了[编译错误])。
monad。它提供了throwerr
代替您的左
,return
代替您的右。翻转(,)[
,告诉代替您的右。(,)()
。
mapParseResult :: (a -> b) -> ParseResult a -> ParseResult b