如何提供编译时保证,确保我的方法将返回与 Scala 中获取的对象相同的对象?

发布于 2024-11-27 06:58:28 字数 355 浏览 1 评论 0原文

在 Scala 类中,我可以方便地将方法的返回类型声明为 this.type 以保证它将返回与调用它的对象相同的对象:

class Foo {
  def bar: this.type = this
}

有没有一种方法可以类似地指定接受给定 AnyRef 参数的方法将准确返回该引用吗?以下代码片段不提供此保证,我可以返回 A 的任何实例:

def processAndReturn[A <: AnyRef](obj: A): A = {
  // work with obj
  obj
}

In a Scala class, I can conveniently declare the return type of a method to be this.type to guarantee that it will return the same object it is called on:

class Foo {
  def bar: this.type = this
}

Is there a way I can similarly specify that a method that accepts a given AnyRef param will return that reference exactly? The following snippet does not provide this guarantee, I could return any instance of A:

def processAndReturn[A <: AnyRef](obj: A): A = {
  // work with obj
  obj
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

寻梦旅人 2024-12-04 06:58:28

huitseeker 的建议实际上对 -Ydependent-method-types 选项有效:

$ scala -Ydependent-method-types
Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_24).

scala>   def processAndReturn[A <: AnyRef](obj: A): obj.type = {
     |       // work with obj
     |       obj
     |   }
processAndReturn: [A <: AnyRef](obj: A)obj.type

scala> processAndReturn("hi")
res1: java.lang.String = hi

来自 在这个线程中,我发现实验性依赖方法类型已经存在了一段时间,甚至有一篇已发表的论文使用它们,但实现可能存在错误。关于重新设计 Scala 类型系统基础存在一些讨论(请参阅 Adriaan Moor 的 幻灯片依赖对象类型)。如果成功的话,我想依赖方法类型(除其他外)在 Scala 的未来版本中将是完全合规的。

huitseeker's suggestion is actually valid with the -Ydependent-method-types option:

$ scala -Ydependent-method-types
Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_24).

scala>   def processAndReturn[A <: AnyRef](obj: A): obj.type = {
     |       // work with obj
     |       obj
     |   }
processAndReturn: [A <: AnyRef](obj: A)obj.type

scala> processAndReturn("hi")
res1: java.lang.String = hi

From this thread, I gather that experimental dependent method types have been around for a while, and there's even a published paper using them, but the implementation may be buggy. There has been some discussion about reworking Scala's type system foundations (see Adriaan Moor's slides on Dependent Object Types). If it works out, I imagine that dependent method types (among other things) will be completely kosher in future versions of Scala.

幸福丶如此 2024-12-04 06:58:28

我想您可能希望在上面的方法中给出返回类型 obj.type ,但不幸的是,目前这是禁止的。根据规范,第 47 页

(...) 目前单例类型的方法参数只能出现在方法中
身体;因此不支持依赖方法类型。

可能有某种方法可以实现您想要的,但不是在该方法本身的类型规范中。

I guess you would want to give the return type obj.type in your method above, but this is unfortunately prohibited as of now. As per the spec, p.47:

(...) at present singleton types of method parameters may only appear in the method
body; so dependent method types are not supported.

There may be some way to achieve what you want, but not in the type specification of that method by itself.

做个少女永远怀春 2024-12-04 06:58:28

huitseeker 和 Kipton 可能有您正在寻找的直接答案。

话虽如此,我能想象的唯一用例是,如果您想链接对同一对象的调用并确保您确实在同一对象上工作。还有其他的吗?

例如:

class Counter {
  var i = 0
  override def toString = i.toString
}

def incr(c: Counter): Counter = { c.i += 1; c }

val two = incr(incr((new Counter)))
// two: Counter = 2

如果我连续调用 incr 两次,我希望得到两次。现在让我们想象一下我正在使用一个行为不当的函数,例如:

def incorrect_incr(c: Counter): Counter = { 
  c.i += 1
  new Counter // should return c but does not
}

val notTwo = incorrect_incr(incorrect_incr(new Counter))
// notTwo: Counter = 0

我认为使用包装器来确保我们继续处理同一个对象可以解决这个问题(在考虑如何使其更惯用后进行编辑):

case class Id[T <: AnyRef](t:T) {
  def map[U](f: (T) => U): Id[T] = { 
    f(t)
    this
  }
  def get: T = t
}

val reallyTwo = Id(new Counter).map(incorrect_incr).map(incorrect_incr).get
// reallyTwo: Counter = 2

map< /code> 基本上确保调用该函数是为了产生副作用,get 解包该值...

huitseeker and Kipton probably have the direct answer you're looking for.

With that said, the only use case I can imagine is if you want to chain calls on the same object and ensure you're really working on the same object. Are there other?

For instance:

class Counter {
  var i = 0
  override def toString = i.toString
}

def incr(c: Counter): Counter = { c.i += 1; c }

val two = incr(incr((new Counter)))
// two: Counter = 2

If I call incr twice in a row, I expect to get two. Now let's imagine I am working with an ill-behaved function like:

def incorrect_incr(c: Counter): Counter = { 
  c.i += 1
  new Counter // should return c but does not
}

val notTwo = incorrect_incr(incorrect_incr(new Counter))
// notTwo: Counter = 0

I think using a wrapper to ensure we keep working on the same object would solve this problem (edited after thinking on how to make this more idiomatic):

case class Id[T <: AnyRef](t:T) {
  def map[U](f: (T) => U): Id[T] = { 
    f(t)
    this
  }
  def get: T = t
}

val reallyTwo = Id(new Counter).map(incorrect_incr).map(incorrect_incr).get
// reallyTwo: Counter = 2

map basically ensures the function is called for its side effect, get unwraps the value...

嘦怹 2024-12-04 06:58:28

问题是:如果你要返回传入的内容,为什么不直接使用 void 呢?毕竟,您已经在调用代码中拥有该对象,不是吗?

The question is: if you're going to return what you passed in, why not just use void? After all, you already have the object in the calling code, don't you?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文