Scala 从左到右的参数类型推断
我有一个案例,我希望基于(几个,比如说,5到10个)选项的存在对对象进行修改。因此,基本上,如果我必须这样做,我的目标是: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) } (请注意:也许我的对
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)(_)
}