是否有一个函数分别使用两个转换函数来转换/映射“左”和“右”情况?

问题描述

我在Scala或Haskell中没有找到可以同时变换/映射Either的{​​{1}}和Left情况的函数,同时具有两个变换函数,即一个函数该类型的

Right

对于Scala中的(A => C,B => D) => Either[C,D] 或类型

Either[A,B]

在Haskell中。在Scala中,这等效于像这样调用(a -> c,b -> d) -> Either a b -> Either c d

fold

或者在Haskell中,这等效于像这样调用def mapLeftOrRight[A,B,C,D](e: Either[A,B],fa: A => C,fb: B => D): Either[C,D] = e.fold(a => Left(fa(a)),b => Right(fb(b)))

either

库中是否存在这样的功能?如果不是,我认为类似的做法很实用,为什么语言设计师选择不将其放在此处?

解决方法

不了解Scala,但是Haskell具有用于类型签名的搜索引擎。它不会为您编写的结果提供结果,但这仅仅是因为您采用了元组参数,而Haskell函数按照惯例是curried https://hoogle.haskell.org/?hoogle=(a -> c) -> (b -> d) -> Either a b -> Either c d确实进行了匹配,最明显的是:

Mongo

...实际上,即使是Google也会发现这一点,因为类型变量恰好与您想像的一样。 (Hoogle还找到了mapBoth。)

但是实际上,正如Martijn所说,if you write it (x -> y) -> (p -> q) -> Either x p -> Either y q :: (a -> c) -> (b -> d) -> Either a b -> Either c d的这种行为只是 bifunctor 的特例,实际上,Hoogle还为您提供了更通用的形式,该形式在Either库:

base

TBH我有点失望,因为H​​oogle本身并没有搞清楚签名或交换参数。可以肯定的是,它实际上曾经自动执行过,但是在某些时候,他们简化了算法,因为库数量巨大,花费的时间和结果变得无法控制。

,

您正在谈论的行为是双向行为,通常被称为双向映射。在Haskell中,可以使用任一功能的https://hackage.haskell.org/package/bifunctors-5/docs/Data-Bifunctor.html

除了显示的折叠之外,scala中的另一种实现是either.map(fb).left.map(fa)

scala stdlib中没有这样的方法,可能是因为找不到有用或根本不够的方法。我可以在某种程度上与之相关:在一个操作中映射双方而不是分别映射双方都没有足够的基本性或有用性,不能保证我将scala stdlib包含在内。不过,该功能在Cats中可用。

在Haskell中,该方法以mapBoth的形式存在于Either上,并且BiFunctor位于基数中。

,

例如,猫提供Bifunctor

import cats.implicits._

val e: Either[String,Int] = Right(41)
e.bimap(e => s"boom: $e",v => 1 + v)
// res0: Either[String,Int] = Right(42)
,

在Haskell中,您可以使用Control.Arrow.(+++),它可以在任何[ { sku: 'sku1',location: 'Chicago',quantity: 3,},{ sku: 'sku2',location: 'New York',quantity: 4,] 上使用:

ArrowChoice

专门用于功能箭头(+++) :: (ArrowChoice arr) => arr a b -> arr c d -> arr (Either a c) (Either b d) infixr 2 +++ ,即:

arr ~ (->)

如果您搜索专用于函数的类型,Hoogle将不会找到(+++) :: (a -> b) -> (c -> d) -> Either a c -> Either b d ,但是您可以通过将所需签名中的+++替换为类型变量来找到这样的通用运算符:{{ 3}}。

用法示例:

->
renderResults
  :: FilePath
  -> Int
  -> Int
  -> [Either String Int]
  -> [Either String String]
renderResults file line column
  = fmap ((prefix ++) +++ show)
  where
    prefix = concat [file,":",show line,show column,": error: "]

还有相关的运算符x a c -> x b d -> x (Either a b) (Either c d)并没有用renderResults "test" 12 34 [Right 1,Left "beans",Right 2,Left "bears"] == [ Right "1",Left "test:12:34: error: beans",Right "2",Left "test:12:34: error: bears" ] 标记结果:

Either

专门用于(|||) :: arr a c -> a b c -> arr (Either a b) c infixr 2 |||

(->)

示例:

(|||) :: (a -> c) -> (b -> c) -> Either a b -> c
assertRights :: [Either String a] -> [a]
assertRights = fmap (error ||| id)

sum $ assertRights [Right 1,Right 2] == 3 sum $ assertRights [Right 1,Left "oh no"] == error "oh no" 是Haskell (|||)either函数的概括,用于在Prelude上进行匹配。用在箭头Either表示法中对ifcase的删除中。