从左到右的参数类型推断

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))

为了让它工作,我可以:

  1. 主动指定 size 参数的类型:my.optionally(option, (size: Int, value) => value.resized(size))
  2. optionally 重写为柯里化版本

None 他们真的很糟糕,但如果我可以问:

考虑需要添加类型的更好方法是这样写:

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 的柯里化版本,就像你说的那样。