Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 使用特定类型重新定义其中一个的实例_Haskell - Fatal编程技术网

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
    

Make
ParseResult
a
newtype
而不是类型别名。这样,您可以在其上定义自己的实例,而不会与已为
定义的实例冲突。如果您可以将数据类型更改为
或[CompilerError]([CompilerWarning],a)
,您可以简单地
fmap
两次:
(fmap.fmap)(+1)(右([],1))
计算为
右([],2)
。我真的不明白TypeSynonymInstances是如何引起如此多的混乱的。它所做的只是用扩展替换实例头中的类型同义词。你也可以自己做,所以你永远不需要TypeSynonymInstances。除了重新定义的罪恶,绝对没有办法定义
函子
实例e对于<代码>要么<代码>,要么做你想要的。我敦促你切换到一个新类型,或者甚至是一个没有<代码>的数据类型< < /C> >,并考虑你想在哪里提取抽象线,使你的包有意义,并尽可能多地重用现有代码。你可能喜欢现有的<代码>文字[CuffeleRebug ](除了[编译错误])。
monad。它提供了
throwerr
代替您的
return
代替您的
右。
翻转(,)[
,告诉
代替您的
右。(,)()
mapParseResult :: (a -> b) -> ParseResult a -> ParseResult b