从左到右的参数类型推断
Left to right arguments type inference
我有一个案例,我希望根据存在的(几个,比如 5 到 10 个)可选对象对对象应用修改。所以基本上,如果我强制执行,我的目标是:
var myObject = ...
if (option.isDefined) {
myObject = myObject.modify(option.get)
}
if (option2.isDefined) {
myObject = myObject.someOtherModification(option2.get)
}
(请注意:也许我的对象是可变的,也许不是,这不是重点。)
我认为如果我尝试实现一种流畅的编写方式会更好看,例如(伪代码...):
myObject.optionally(option, _.modify(_))
.optionally(option2, _.someOtherModification(_))
所以我从一个示例代码开始,intelliJ 没有将其突出显示为错误,但实际上并没有构建。
class MyObject(content: String) {
/** Apply a transformation if the optional is present */
def optionally[A](optional: Option[A], operation: (A, MyObject) => MyObject): MyObject =
optional.map(operation(_, this)).getOrElse(this)
/** Some possible transformation */
def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
object Test {
val my = new MyObject("test")
val option = Option(2)
my.optionally(option, (size, value) => value.resized(size))
}
现在,在我的例子中,MyObject
类型属于某些外部 API,因此我创建了一个隐式转换来提供帮助,所以它的实际情况如下:
// Out of my control
class MyObject(content: String) {
def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
// What I did : create a rich type over MyObject
class MyRichObject(myObject: MyObject) {
def optionally[A](optional: Option[A], operation: (A, MyObject) => MyObject): MyObject = optional.map(operation(_, myObject)).getOrElse(myObject)
}
// And an implicit conversion
object MyRichObject {
implicit def apply(myObject: MyObject): MyRichObject = new MyRichObject(myObject)
}
然后,我这样使用它:
object Test {
val my = new MyObject("test")
val option = Option(2)
import MyRichObject._
my.optionally(option, (size, value) => value.resized(size))
}
这一次,它在 IntelliJ 中和编译时失败了,因为 Option
的类型未知:
Error:(8, 26) missing parameter type
my.optionally(option, (size, value) => value.resized(size))
为了让它工作,我可以:
- 主动指定
size
参数的类型:my.optionally(option, (size: Int, value) => value.resized(size))
- 将
optionally
重写为柯里化版本
None 他们真的很糟糕,但如果我可以问:
- 柯里化版本有效但多参数版本似乎无法推断参数化类型是否有原因,
- 是否可以在不指定实际类型的情况下以一种有效的方式编写
- 作为奖励(虽然这可能是基于意见的),你会怎么写它(我想到了关于一系列可选的某种
foldLeft
...)?
考虑需要添加类型的更好方法是这样写:
object Test {
val my = new MyObject("test")
val option = Some(2)
my.optionally[Int](option, (size, value) => value.resized(size))
}
另一种方法,如果自创建对象后您只管理一个类型,则将泛型移至 class 创建,但要小心,使用此选项每个实例只能有一种类型:
class MyObject[A](content: String) {
def optionally(optional: Option[A], operation: (A, MyObject[A]) => MyObject[A]): MyObject[A] =
optional.map(operation(_, this)).getOrElse(this)
def resized(length : Int): MyObject[A] = new MyObject[A](content.substring(0, length))
}
object Test {
val my = new MyObject[Int]("test")
val option = Some(2)
my.optionally(option, (size, value) => value.resized(size))
}
如您所见,现在所有泛型所在的地方都被 Int 类型占用了,因为这正是您最初想要的,这里有一个漂亮的答案说明了原因:
(只是我认为适用于此的部分:)
4)When the inferred return type would be more general than you intended, e.g., Any.
来源:
供您考虑的一个选项:
// Out of my control
class MyObject(content: String) {
def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
object MyObjectImplicits {
implicit class OptionalUpdate[A](val optional: Option[A]) extends AnyVal {
def update(operation: (A, MyObject) => MyObject): MyObject => MyObject =
(obj: MyObject) => optional.map(a => operation(a, obj)).getOrElse(obj)
}
}
object Test {
val my = new MyObject("test")
val option = Option(2)
import MyObjectImplicits._
Seq(
option.update((size, value) => value.resized(size)),
// more options...
).foldLeft(my)(_)
}
不如使用你的 optionally
的柯里化版本,就像你说的那样。
我有一个案例,我希望根据存在的(几个,比如 5 到 10 个)可选对象对对象应用修改。所以基本上,如果我强制执行,我的目标是:
var myObject = ...
if (option.isDefined) {
myObject = myObject.modify(option.get)
}
if (option2.isDefined) {
myObject = myObject.someOtherModification(option2.get)
}
(请注意:也许我的对象是可变的,也许不是,这不是重点。)
我认为如果我尝试实现一种流畅的编写方式会更好看,例如(伪代码...):
myObject.optionally(option, _.modify(_))
.optionally(option2, _.someOtherModification(_))
所以我从一个示例代码开始,intelliJ 没有将其突出显示为错误,但实际上并没有构建。
class MyObject(content: String) {
/** Apply a transformation if the optional is present */
def optionally[A](optional: Option[A], operation: (A, MyObject) => MyObject): MyObject =
optional.map(operation(_, this)).getOrElse(this)
/** Some possible transformation */
def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
object Test {
val my = new MyObject("test")
val option = Option(2)
my.optionally(option, (size, value) => value.resized(size))
}
现在,在我的例子中,MyObject
类型属于某些外部 API,因此我创建了一个隐式转换来提供帮助,所以它的实际情况如下:
// Out of my control
class MyObject(content: String) {
def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
// What I did : create a rich type over MyObject
class MyRichObject(myObject: MyObject) {
def optionally[A](optional: Option[A], operation: (A, MyObject) => MyObject): MyObject = optional.map(operation(_, myObject)).getOrElse(myObject)
}
// And an implicit conversion
object MyRichObject {
implicit def apply(myObject: MyObject): MyRichObject = new MyRichObject(myObject)
}
然后,我这样使用它:
object Test {
val my = new MyObject("test")
val option = Option(2)
import MyRichObject._
my.optionally(option, (size, value) => value.resized(size))
}
这一次,它在 IntelliJ 中和编译时失败了,因为 Option
的类型未知:
Error:(8, 26) missing parameter type
my.optionally(option, (size, value) => value.resized(size))
为了让它工作,我可以:
- 主动指定
size
参数的类型:my.optionally(option, (size: Int, value) => value.resized(size))
- 将
optionally
重写为柯里化版本
None 他们真的很糟糕,但如果我可以问:
- 柯里化版本有效但多参数版本似乎无法推断参数化类型是否有原因,
- 是否可以在不指定实际类型的情况下以一种有效的方式编写
- 作为奖励(虽然这可能是基于意见的),你会怎么写它(我想到了关于一系列可选的某种
foldLeft
...)?
考虑需要添加类型的更好方法是这样写:
object Test {
val my = new MyObject("test")
val option = Some(2)
my.optionally[Int](option, (size, value) => value.resized(size))
}
另一种方法,如果自创建对象后您只管理一个类型,则将泛型移至 class 创建,但要小心,使用此选项每个实例只能有一种类型:
class MyObject[A](content: String) {
def optionally(optional: Option[A], operation: (A, MyObject[A]) => MyObject[A]): MyObject[A] =
optional.map(operation(_, this)).getOrElse(this)
def resized(length : Int): MyObject[A] = new MyObject[A](content.substring(0, length))
}
object Test {
val my = new MyObject[Int]("test")
val option = Some(2)
my.optionally(option, (size, value) => value.resized(size))
}
如您所见,现在所有泛型所在的地方都被 Int 类型占用了,因为这正是您最初想要的,这里有一个漂亮的答案说明了原因:
(只是我认为适用于此的部分:)
4)When the inferred return type would be more general than you intended, e.g., Any.
来源:
供您考虑的一个选项:
// Out of my control
class MyObject(content: String) {
def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
object MyObjectImplicits {
implicit class OptionalUpdate[A](val optional: Option[A]) extends AnyVal {
def update(operation: (A, MyObject) => MyObject): MyObject => MyObject =
(obj: MyObject) => optional.map(a => operation(a, obj)).getOrElse(obj)
}
}
object Test {
val my = new MyObject("test")
val option = Option(2)
import MyObjectImplicits._
Seq(
option.update((size, value) => value.resized(size)),
// more options...
).foldLeft(my)(_)
}
不如使用你的 optionally
的柯里化版本,就像你说的那样。