Scala 什么';这两种原油的交易是什么?
这两个类中的任何一个看起来都很有用,使用它的方法也很明显。但当我看到API文档时,我感到困惑: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: <
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))
}
}