Scala 什么';这两种原油的交易是什么?

Scala 什么';这两种原油的交易是什么?,scala,Scala,这两个类中的任何一个看起来都很有用,使用它的方法也很明显。但当我看到API文档时,我感到困惑: def joinLeft [A1 >: A, B1 >: B, C] (implicit ev: <:<[A1, Either[C, B1]]): Either[C, B1] Joins an Either through Left. def joinRight [A1 >: A, B1 >: B, C] (implicit ev: <

这两个类中的任何一个看起来都很有用,使用它的方法也很明显。但当我看到API文档时,我感到困惑:

def joinLeft [A1 >: A, B1 >: B, C] (implicit ev: <:<[A1, Either[C, B1]]):
         Either[C, B1]
   Joins an Either through Left.

def joinRight [A1 >: A, B1 >: B, C] (implicit ev: <:<[B1, Either[A1, C]]):
         Either[A1, C]
   Joins an Either through Right.

def left : LeftProjection[A, B]
   Projects this Either as a Left.

def right : RightProjection[A, B]
   Projects this Either as a Right.

def joinLeft[A1>:A,B1>:B,C](隐式ev::B,C)(隐式ev:
joinLeft
joinRight
使您能够“展平”嵌套的

scala> val e: Either[Either[String, Int], Int] = Left(Left("foo"))
e: Either[Either[String,Int],Int] = Left(Left(foo))

scala> e.joinLeft
res2: Either[String,Int] = Left(foo)

编辑:显示如何使用投影的一个示例,在本例中,可以在不匹配模式或调用
isLeft
isRight
的情况下将
序列折叠在一起。如果您熟悉如何在不匹配或调用
isDefined
的情况下使用
选项


当我好奇地观察当前情况时,我发现
joinLeft
joinRight
是通过模式匹配实现的。然而,我偶然发现了这一点,并发现它用于使用投影实现连接方法:

def joinLeft[A, B](es: Either[Either[A, B], B]) =
  es.left.flatMap(x => x)

现在忽略连接,投影是一种机制,允许您使用
作为monad。可以将其视为将左侧或右侧提取到
选项中,但不会丢失另一侧

与往常一样,这在示例中可能更有意义。因此,假设您有一个
或[Exception,Int]
,并希望将
异常
转换为
字符串(如果存在)


这将映射到结果的左侧,为您提供一个
或[String,Int]
是重要的两个方向。
在没有投影的情况下很有用(大多数情况下您进行模式匹配),但投影非常值得注意,因为它们提供了更丰富的API。您将使用更少的连接

通常用于表示“正确的值或错误”。在这方面,它就像一个扩展的
选项
。如果没有数据,而不是
,则会出现错误。
选项
具有丰富的API。如果我们知道其中哪一个是结果,哪一个是错误,则可以在
上提供相同的API


投影正好说明了这一点。它是
,加上附加的知识,即值分别位于左侧或右侧,另一个是错误

例如,在
Option
中,您可以映射,因此
opt.map(f)
返回一个
选项
,其中
f
应用于
opt
的值(如果它有一个),如果
opt
None
,则仍然
None
。在左侧投影上,如果它是
左侧
,则将
f
应用于左侧的值,如果它是
右侧
,则保持不变。观察保留签名:

  • LeftProjection[A,B]
    中,
    map[C](f:A=>C):或者[C,B]
  • RightProjection[A,B]
    中,
    map[C](f:B=>C):要么[A,C]

left
right
只是当您想要使用一个常用的API例程时,表示哪一侧被认为是值的方法

替代方案可以是:

  • 设置一个约定,如在Haskell中,有很强的语法原因将值置于正确位置。当您想在另一端应用一个方法时(例如,您可能想使用
    映射更改错误),请在前后进行
    交换
  • 后缀方法名称为左或右(可能只有L和R)。这将阻止用于理解。使用
    for
    理解(
    flatMap
    事实上,但是for表示法非常方便)
    或者
    都是(选中的)异常的替代方法

现在,连接。左和右表示与投影相同的事物,它们与<代码>平面图< /代码>密切相关。请考虑<代码>连接左> /代码>。签名可能令人费解:

joinLeft [A1 >: A, B1 >: B, C] (implicit ev: <:<[A1, Either[C, B1]]):
         Either[C, B1]

什么隐含的方法是,如果代码< > < <代码> >是<代码> [C,b] < /代码>。该方法在<代码>中不可用[A],[B] 一般,但只在<代码> [[C,B],B] < /代码>。如用左投影,我们认为值在左边(这将是正确的<代码>连接权< /代码>)。。连接所做的是将其展平(想想

flatMap
)。当一个连接时,我们不关心错误(B)是在内部还是外部,我们只需要[C,B]。因此左(左(C))产生左(C),左(右(B))和右(B)产生右(B)。与flatMap的关系如下:

joinLeft(e) = e.left.flatMap(identity)
e.left.flatMap(f) = e.left.map(f).joinLeft

选项
等价物将在
选项[Option[A]]
上工作,
一些(Some(x))
将产生
一些(x)
两个
部分(None)
将产生
。它可以写为平面图(identity)。注意
选项[A]
任一[A,Unit]
同构(如果使用左投影和连接)以及
任一[Unit,A]
(使用右投影)。

我的建议是将以下内容添加到实用程序包中:

implicit class EitherRichClass[A, B](thisEither: Either[A, B])
{
   def map[C](f: B => C): Either[A, C] = thisEither match
   {
     case Left(l) => Left[A, C](l)
     case Right(r) => Right[A, C](f(r))
   }
   def flatMap[C](f: B => Either[A, C]): Either[A, C] = thisEither match
   {
     case Left(l) => Left[A, C](l)
     case Right(r) => (f(r))
   }
}   
根据我的经验,提供的唯一有用的方法是fold。你在函数代码中并没有真正使用isLeft或isRight。joinLeft和joinRight可能会像Dider Dupont所解释的那样用作展平函数,但是,我还没有机会这样使用它们。上面提到的两种方法都是右偏的,我怀疑这是大多数人使用它们的方式。它它就像一个带有错误值而不是无的选项

这是我自己的一些代码。抱歉,它不是经过修饰的代码,但它是一个用于理解的示例。将map和flatMap方法添加到其中允许我们使用中的特殊语法进行理解。它解析HTTP头,返回HTTP和Html错误页响应或解析的自定义HTTP请求对象。Wit如果要理解代码,使用
joinLeft(e) = e.left.flatMap(identity)
e.left.flatMap(f) = e.left.map(f).joinLeft
implicit class EitherRichClass[A, B](thisEither: Either[A, B])
{
   def map[C](f: B => C): Either[A, C] = thisEither match
   {
     case Left(l) => Left[A, C](l)
     case Right(r) => Right[A, C](f(r))
   }
   def flatMap[C](f: B => Either[A, C]): Either[A, C] = thisEither match
   {
     case Left(l) => Left[A, C](l)
     case Right(r) => (f(r))
   }
}   
object getReq
{      
  def LeftError[B](str: String) = Left[HResponse, B](HttpError(str))
  def apply(line1: String, in: java.io.BufferedReader): Either[HResponse, HttpReq] = 
  {
    def loop(acc: Seq[(String, String)]): Either[HResponse, Seq[(String, String)]] =
    {
      val ln = in.readLine
      if (ln == "")
        Right(acc)         
      else
        ln.splitOut(':', s => LeftError("400 Bad Syntax in Header Field"), (a, b) => loop(acc :+ Tuple2(a.toLowerCase, b)))
    }

    val words: Seq[String] = line1.lowerWords

    for
    {
      a3 <- words match
      {
        case Seq("get", b, c) => Right[HResponse, (ReqType.Value, String, String)]((ReqType.HGet, b, c))
        case Seq("post", b, c) => Right[HResponse, (ReqType.Value, String, String)]((ReqType.HPost, b, c))
        case Seq(methodName, b, c) => LeftError("405" -- methodName -- "method not Allowed")
        case _ => LeftError("400 Bad Request: Bad Syntax in Status Line")
      }
      val (reqType, target, version) = a3
      fields <- loop(Nil)
      val optLen = fields.find(_._1 == "content-length")
      pair <- optLen match
      {
        case None => Right((0, fields))
        case Some(("content-length", second)) => second.filterNot(_.isWhitespace) match
        {
          case s if s.forall(_.isDigit) => Right((s.toInt, fields.filterNot(_._1 == "content-length")))
          case s => LeftError("400 Bad Request: Bad Content-Length SyntaxLine")
        }
      }
      val (bodyLen, otherHeaderPairs) = pair
      val otherHeaderFields = otherHeaderPairs.map(pair => HeaderField(pair._1, pair._2))
      val body = if (bodyLen > 0) (for (i <- 1 to bodyLen) yield in.read.toChar).mkString else ""         
    }      
    yield (HttpReq(reqType, target, version, otherHeaderFields, bodyLen, body))
  }   
}