何时在 Scala 方法中使用下限
when to use lower bound in scala method
我一直在复习 Scala 知识,一直在绕 variance/lower 界。
在'functional programming in scala'书中,Either类型,它有以下signature/exercise(flatMap的实现版本,orElse在Either上运行在右边值)。
sealed trait Either[+E,+A] {
def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = ???
def orElse[EE >: E, B >: A](b: => Either[EE, B]): Either[EE, B] = ???
}
这本书的注释说
when mapping over the right side, we must promote the left type parameter to some supertype, to satisfy the +E variance annotation. similarly for 'orElse'
我的问题是:
- 为什么不是我们必须在
flatMap
函数中说B >: A
?我们不需要满足 +A
?
- 为什么
orElse
签名需要 B >: A
?
我理解方法参数算作逆变位置,所以我们不可能在方法参数中包含 A
或 E
。即 f
或 b
的 'return type' 中不能有 E
或 A
。
也许我遗漏了一些关于 subtyping/lower bound/function 作为参数的基础知识。
请用一些具体的例子帮助我理解它。
p.s。大多数关于方差或 upper/lower 边界的文章,我发现 class/trait.
中只有 1 个类型参数
why do not we have to say B >: A
in the flatMap function? we do not
need to satisfy +A
?
flatMap
不会对 f: A => Either[EE, B]
生成的类型施加任何限制。这意味着,例如,我们可以有一个 Either[Throwable, String]
并使用 flatMap
将其转换为 Either[Throwable, Int]
。请注意 String
和 Int
之间的唯一关系是通过 Any
.
why does orElse signature requires B >: A
当我们说:"Give me the left hand side, or else give me the right hand side"时,我们通常希望两种类型对齐,这样我们的"fallback",通过orElse
,将提供一个有意义的回退。
例如,让我们使用上面的例子,假设我们想要一个 Either[Throwable, String]
并使用 flatMap
:
将其转换为 Either[Throwable, Int]
val either: Either[Throwable, String] = Right("42")
val res: Either[Throwable, Int] = either.flatMap(str => Try(str.toInt).toEither)
当我们的 String
为 42 时,这将起作用,但如果它不是有效的 Int
,我们将返回 Left[Throwable]
。现在让我们决定,如果解析失败,我们总是希望 return -1 作为默认值(当然有更好的方法来对此建模,但坚持我的看法)。为此,我们可以利用 orElse
:
val either: Either[Throwable, String] = Right("42")
val res: Either[Throwable, Int] = either.flatMap(str => Try(str.toInt).toEither).orElse(Right(-1))
这样,LHS 和 RHS 之间的关系得以保留,我们收到一个合理的值作为结果。如果 B
根本不受限于 A
,我们通常会在类型层次结构中得到一个超类型,例如 AnyRef
或 Any
.
关于 EE >: E
约束的另一件事。由于 E
是 协变的 ,如果我们尝试将其用作 flatMap
函数的类型参数:
sealed trait Either[+E, +A] {
def flatMap[B](f: A => Either[E, B]): Either[E, B] = ???
}
编译器会对我们大喊:
Error:(7, 20) covariant type E occurs in contravariant position in
type A => Either[E,B] of value f
def flatMap[B](f: A => Either[E, B]): Either[E, B] = ???
那是因为协变类型不能"go in"到方法中,只能用在return类型中,而逆变类型参数"go in"相反,不能用在return类型中结果类型。
如果 Either
不变,签名将是
sealed trait Either[E,A] {
def flatMap[B](f: A => Either[E, B]): Either[E, B] = ???
def orElse(b: => Either[E, A]): Either[E, A] = ???
}
这里A
和B
没有联系
现在如果我们使 Either
与 E
协变,我们必须添加 EE >: E
sealed trait Either[+E,A] {
def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = ???
def orElse[EE >: E](b: => Either[EE, A]): Either[EE, A] = ???
}
否则,如果我们使 Either
关于 A
协变,我们必须添加 AA >: A
sealed trait Either[E,+A] {
def flatMap[B](f: A => Either[E, B]): Either[E, B] = ???
def orElse[AA >: A](b: => Either[E, AA]): Either[E, AA] = ???
}
只是 AA
表示为 B
。
在实际情况下,Either
对于两种类型参数都是协变的,因此这是上述的组合。
我想现在很明显 flatMap
中的 B
和 orElse
中的 B
是不同的。
我一直在复习 Scala 知识,一直在绕 variance/lower 界。
在'functional programming in scala'书中,Either类型,它有以下signature/exercise(flatMap的实现版本,orElse在Either上运行在右边值)。
sealed trait Either[+E,+A] {
def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = ???
def orElse[EE >: E, B >: A](b: => Either[EE, B]): Either[EE, B] = ???
}
这本书的注释说
when mapping over the right side, we must promote the left type parameter to some supertype, to satisfy the +E variance annotation. similarly for 'orElse'
我的问题是:
- 为什么不是我们必须在
flatMap
函数中说B >: A
?我们不需要满足+A
? - 为什么
orElse
签名需要B >: A
?
我理解方法参数算作逆变位置,所以我们不可能在方法参数中包含 A
或 E
。即 f
或 b
的 'return type' 中不能有 E
或 A
。
也许我遗漏了一些关于 subtyping/lower bound/function 作为参数的基础知识。
请用一些具体的例子帮助我理解它。
p.s。大多数关于方差或 upper/lower 边界的文章,我发现 class/trait.
中只有 1 个类型参数why do not we have to say
B >: A
in the flatMap function? we do not need to satisfy+A
?
flatMap
不会对 f: A => Either[EE, B]
生成的类型施加任何限制。这意味着,例如,我们可以有一个 Either[Throwable, String]
并使用 flatMap
将其转换为 Either[Throwable, Int]
。请注意 String
和 Int
之间的唯一关系是通过 Any
.
why does orElse signature requires B >: A
当我们说:"Give me the left hand side, or else give me the right hand side"时,我们通常希望两种类型对齐,这样我们的"fallback",通过orElse
,将提供一个有意义的回退。
例如,让我们使用上面的例子,假设我们想要一个 Either[Throwable, String]
并使用 flatMap
:
Either[Throwable, Int]
val either: Either[Throwable, String] = Right("42")
val res: Either[Throwable, Int] = either.flatMap(str => Try(str.toInt).toEither)
当我们的 String
为 42 时,这将起作用,但如果它不是有效的 Int
,我们将返回 Left[Throwable]
。现在让我们决定,如果解析失败,我们总是希望 return -1 作为默认值(当然有更好的方法来对此建模,但坚持我的看法)。为此,我们可以利用 orElse
:
val either: Either[Throwable, String] = Right("42")
val res: Either[Throwable, Int] = either.flatMap(str => Try(str.toInt).toEither).orElse(Right(-1))
这样,LHS 和 RHS 之间的关系得以保留,我们收到一个合理的值作为结果。如果 B
根本不受限于 A
,我们通常会在类型层次结构中得到一个超类型,例如 AnyRef
或 Any
.
关于 EE >: E
约束的另一件事。由于 E
是 协变的 ,如果我们尝试将其用作 flatMap
函数的类型参数:
sealed trait Either[+E, +A] {
def flatMap[B](f: A => Either[E, B]): Either[E, B] = ???
}
编译器会对我们大喊:
Error:(7, 20) covariant type E occurs in contravariant position in type A => Either[E,B] of value f def flatMap[B](f: A => Either[E, B]): Either[E, B] = ???
那是因为协变类型不能"go in"到方法中,只能用在return类型中,而逆变类型参数"go in"相反,不能用在return类型中结果类型。
如果 Either
不变,签名将是
sealed trait Either[E,A] {
def flatMap[B](f: A => Either[E, B]): Either[E, B] = ???
def orElse(b: => Either[E, A]): Either[E, A] = ???
}
这里A
和B
没有联系
现在如果我们使 Either
与 E
协变,我们必须添加 EE >: E
sealed trait Either[+E,A] {
def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = ???
def orElse[EE >: E](b: => Either[EE, A]): Either[EE, A] = ???
}
否则,如果我们使 Either
关于 A
协变,我们必须添加 AA >: A
sealed trait Either[E,+A] {
def flatMap[B](f: A => Either[E, B]): Either[E, B] = ???
def orElse[AA >: A](b: => Either[E, AA]): Either[E, AA] = ???
}
只是 AA
表示为 B
。
在实际情况下,Either
对于两种类型参数都是协变的,因此这是上述的组合。
我想现在很明显 flatMap
中的 B
和 orElse
中的 B
是不同的。