Scala 从左到右的参数类型推断

Scala 从左到右的参数类型推断,scala,implicit-conversion,type-inference,Scala,Implicit Conversion,Type Inference,我有一个案例,我希望基于(几个,比如说,5到10个)选项的存在对对象进行修改。因此,基本上,如果我必须这样做,我的目标是: var myObject = ... if (option.isDefined) { myObject = myObject.modify(option.get) } if (option2.isDefined) { myObject = myObject.someOtherModification(option2.get) } (请注意:也许我的对

我有一个案例,我希望基于(几个,比如说,5到10个)选项的存在对对象进行修改。因此,基本上,如果我必须这样做,我的目标是:

var myObject = ...
if (option.isDefined) {
    myObject = myObject.modify(option.get)   
}
if (option2.isDefined) {
    myObject = myObject.someOtherModification(option2.get)
}
(请注意:也许我的对象是可变的,也许不是,这不是重点。)

我认为如果我尝试实现一种流畅的编写方法,比如(伪代码…)会更好:

因此,我从一个示例代码开始,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和编译时失败,因为
选项的类型未知:
错误:(8,26)缺少参数类型
my.option(选项,(大小,值)=>value.resized(大小))

要使其发挥作用,我可以:

  • 主动指定
    size
    参数的类型:
    my.option(选项,(size:Int,value)=>value.resized(size))
  • 可选择将
    重写为当前版本
  • 这些都不是很糟糕,但如果我可以问:

    • 咖喱版本有效,但多参数版本似乎无法推断参数化类型,这有什么原因吗
    • 它是否可以在不指定实际类型的情况下进行编写
    • 作为奖励(尽管这可能是基于观点的),您将如何编写它(我想到的是一系列选项上的某种
      foldLeft

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

    object Test {
      val my = new MyObject("test")
      val option = Some(2)
      my.optionally[Int](option, (size, value) => value.resized(size))
    }
    
    另一种方法是,如果自对象创建以来只管理一种类型,则将泛型移动到类创建中,,但要小心,使用此选项,每个实例只能有一种类型:

    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) 当推断的返回类型比您预期的更一般时,例如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)(_)
    }
    

    就像你说的那样,也可以选择使用你的
    的咖喱版本。

    谢谢你的第一个建议。回想起来似乎很明显,而且有点好。你的第二个提议也不错,但有限制,加上我不能修改MyObject。您的答案的其余部分似乎不适用,因为它没有解释当隐式开始起作用时推理停止工作的原因…@GPI在本例中规则4)适用:4)当推断的返回类型比您预期的更一般时,例如,任何。我是说你在哪里返回一个普通类型,当你想要一个特定类型时,你不同意吗?@GPI无论如何,我删除了不重要的部分来澄清答案,我一直很乐意帮助你。谢谢。我不认为返回类型是罪魁祸首。有问题的是可选参数的类型,而不是任何结果类型,事实上,指定参数的类型对于编译器来说已经足够了。我也看不出它有多普遍,我的返回类型在这里被指定为MyObject,它尽可能具体。我喜欢将隐式转换为可选类型,而不是MyObject(泛化:使参数化类型可转换,从而消除另一端的任何参数化方法)。我同意你的结论:咖喱版看起来至少一样好。让我觉得可以尝试使用见证类型()还有你观察到的类型推断的限制谢谢你挖掘它。我不明白的是,在第一个示例实现中(没有隐式),这实际上是可行的…但是您的第一个示例给了我相同的“缺少参数类型…”错误。好的。。。intelliJ并没有向我强调它,但是尝试实际构建JAR会导致两个示例都出现错误。这使得问题的80%变得毫无意义,因为我试图理解为什么编译器没有对这两个例子进行相同的处理,而实际上是这样的。我会更新这个问题以备将来参考。谢谢
    // 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)(_)
    }