Groovy 的安全取消引用运算符 (?.) 的最佳 Scala 模仿? [英] Best Scala imitation of Groovy's safe-dereference operator (?.)?

查看:11
本文介绍了Groovy 的安全取消引用运算符 (?.) 的最佳 Scala 模仿?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道 Groovy 的 安全取消引用运算符 (?.),或者至少有一些接近的替代方案是?

我已经对其进行了简短的讨论Daniel Spiewak 的博客上,但想将其开放给 StackOverFlow...>

为了大家的时间,这里是丹尼尔的初步回应,我的反驳,以及他的第二次回应:

<块引用>

@安东尼

实际上,我看着做那个第一的.或者更确切地说,我试图复制 Ragenwald 的 andand来自 Ruby 领域的运营商".问题是的,这有点难做没有代理.考虑以下表达式(使用 Ruby 的andand,但它与Groovy 的运算符):

test.andand().doSomething()

我可以创建一个隐式转换从 Any => 某种类型实现andand() 方法,但这就是魔法停止.无论是否为值是否为空,则doSomething() 方法仍然会执行.因为它必须执行以类型安全的方式某些目标,这将需要实施字节码代理,这将是片状和奇怪的(问题注释,最终方法,构造函数等).

更好的选择是回到两者的灵感来源andand 以及 Groovy 的保险箱解引用运算符:一元映射手术.以下是一些Scala使用 Option 实现的语法模式:

val something: Option[String] = ...//大概可以是 Some(...) 或无

val length = something.map(_.length)

在此之后,length 要么是Some(str.length) (其中 str 是包含在字符串中的对象选项),或无.这正是如何安全解除引用操作符工作,除了它使用 null 而不是 a类型安全的单子.

如上所述,我们可以定义某种类型的隐式转换T => Option[T] 然后映射到那个时尚,但有些类型已经有了地图定义,所以它不会很有用.或者,我可以实现类似于 map 的东西,但是有一个单独的名字,但无论如何实施,它将依赖于高阶函数而不是简单的链式调用.好像是只是静态类型的性质语言(如果有人有办法解决这个,请随时纠正我).

Daniel Spiewak 2008 年 7 月 7 日星期一在下午 1:42

我的第二个问题:

<块引用>

感谢丹尼尔的回应关于 ?.我想我错过了!一世我想我明白你是什么提议,但有什么事情像这样,假设你没有控制来源:

company?.getContactPerson?.getContactDetails?.getAddress?.getCity

<块引用>

说它是一个java bean,你不能去在并将返回值更改为某事[T] - 我们可以在那里做什么?

安东尼·斯塔布斯,2009 年 7 月 21 日,星期二晚上 8:07 哦,天哪 - 可以重新阅读这就是你提议的地方从 T 到的隐式转换选项[T] 对吗?但你还会能够将它链接在一起,就像那?你仍然需要地图对吗?唔….

var city = company.map(_.getContactPerson.map(_.getContactDetails.map(_.getAddress.map(_.getCity))))

<块引用>

?

安东尼·斯塔布斯,2009 年 7 月 21 日,星期二晚上 8 点 10 分

他的第二个回应:

<块引用>

@安东尼

我们真的无能为力公司的情况?.getContactPerson,等等......即使假设这是有效的Scala 语法,我们仍然需要一些防止后面调用的方法链.这是不可能的,如果我们是不使用函数值.因此,像地图这样的东西真的是唯一的选项.

隐式转换为 Option不会坏,但通过制作东西隐含的,我们正在规避一些类型系统的保护.这做这种事情的最好方法是一致地使用 for-comprehensions带选项.我们可以做地图和flatMap,但它更好神奇的语法:

 用于 {<- 公司人 <- c.getContactPerson详细信息 <- person.getContactDetails地址 <- details.getAddress} yield address.getCity

<块引用>

丹尼尔·斯皮瓦克,2009 年 7 月 21 日,星期二,晚上 9:28

附言如果丹尼尔在他的博客上发布了他的原始答案作为答案,我将编辑问题以为了系统将它们删除.

解决方案

这个怎么样?

def ?[A](block: => A) =尝试 { 块 } 捕获 {case e: NullPointerException if e.getStackTrace()(2).getMethodName == "$qmark" =>空值情况e =>扔e}

使用这个小片段,您可以安全地取消引用并且代码本身非常简洁:

val a = ?(b.c.d.e)

a == null 如果 b 或 b.c 或 b.c.d 或 b.c.d.e 为空,否则 a == b.c.d.e

我认为,当您使用像 Scala 这样具有按名称调用和隐式等功能的语言时,安全取消引用运算符的价值会降低.

ps:我根据以下注释之一稍微修改了上面的代码,以处理 NullPointerException 为实际上是在被调用函数内部抛出的.

顺便说一句,我认为使用下面的函数是编写 Scala 的一种更惯用的方式:

def ??[A](block: => A): Option[A] = ?(block) match {情况a:A=>一些(一)案例_ =>没有任何}

像这样:

??(a.b.c.d) 匹配 {case Some(result) =>//用结果做更多​​的事情情况无=>//处理空"情况}

I would like to know what the best Scala imitation of Groovy's safe-dereference operator (?.), or at least some close alternatives are?

I've discussed it breifly on Daniel Spiewak's blog, but would like to open it up to StackOverFlow...

For the sake of everyone's time, here is Daniel's initial response, my counter, and his 2nd response:

@Antony

Actually, I looked at doing that one first. Or rather, I was trying to replicate Ragenwald’s andand "operator" from Ruby land. The problem is, this is a bit difficult to do without proxies. Consider the following expression (using Ruby’s andand, but it’s the same with Groovy’s operator):

test.andand().doSomething()

I could create an implicit conversion from Any => some type implementing the andand() method, but that’s where the magic stops. Regardless of whether the value is null or not, the doSomething() method will still execute. Since it has to execute on some target in a type-safe manner, that would require the implementation of a bytecode proxy, which would be flaky and weird (problems with annotations, final methods, constructors, etc).

A better alternative is to go back to the source of inspiration for both andand as well as Groovy’s safe dereference operator: the monadic map operation. The following is some Scala syntax which uses Option to implement the pattern:

val something: Option[String] = … // presumably could be either Some(…) or None

val length = something.map(_.length)

After this, length either be Some(str.length) (where str is the String object contained within the Option), or None. This is exactly how the safe-dereferencing operator works, except it uses null rather than a type-safe monad.

As pointed out above, we could define an implicit conversion from some type T => Option[T] and then map in that fashion, but some types already have map defined, so it wouldn’t be very useful. Alternatively, I could implement something similar to map but with a separate name, but any way it is implemented, it will rely upon a higher-order function rather than a simple chained call. It seems to be just the nature of statically typed languages (if anyone has a way around this, feel free to correct me).

Daniel Spiewak Monday, July 7, 2008 at 1:42 pm

My 2nd question:

Thanks for the response Daniel regarding ?. I think I missed it! I think I understand what you’re proposing, but what about something like this, assuming you don’t have control over the sources:

company?.getContactPerson?.getContactDetails?.getAddress?.getCity

Say it’s a java bean and you can’t go in and change the return values to Something[T] - what can we do there?

Antony Stubbs Tuesday, July 21, 2009 at 8:07 pm oh gosh - ok on re-read that’s where you’re proposing the implicit conversion from T to Option[T] right? But would you still be able to chain it together like that? You’d still need the map right? hmm….

var city = company.map(_.getContactPerson.map(_.getContactDetails.map(_.getAddress.map(_.getCity))))

?

Antony Stubbs Tuesday, July 21, 2009 at 8:10 pm

His 2nd response:

@Antony

We can’t really do much of anything in the case of company?.getContactPerson, etc… Even assuming this were valid Scala syntax, we would still need some way to prevent the later calls in the chain. This is not possible if we’re not using function values. Thus, something like map is really the only option.

An implicit conversion to Option wouldn’t be bad, but by making things implicit, we’re circumventing some of the protection of the type system. The best way to do this sort of thing is to use for-comprehensions in concert with Option. We can do map and flatMap, but it’s much nicer with magical syntax:

 for {
   c < - company
   person <- c.getContactPerson   
   details <- person.getContactDetails
   address <- details.getAddress 
  } yield address.getCity

Daniel Spiewak Tuesday, July 21, 2009 at 9:28 pm

P.s. if Daniel posts his original answers on his blog as answers, I will edit the question to remove them for the sake of the System.

解决方案

How about this?

def ?[A](block: => A) =
  try { block } catch {
    case e: NullPointerException if e.getStackTrace()(2).getMethodName == "$qmark" => null
    case e => throw e
  }

Using this little snippet, you can dereference safely and the code itself is quite succinct:

val a = ?(b.c.d.e)

a == null if b or b.c or b.c.d or b.c.d.e is null, otherwise, a == b.c.d.e

I think the value of a safe-dereference operator is diminished when you are using a language like Scala which has facilities like call-by-name and implicits.

ps: I modify the code above a bit in light of one of the comments below to handle the case when NullPointerException is actually thrown inside the called function.

BTW, I think using the function below is a more idiomatic way of writing Scala:

def ??[A](block: => A): Option[A] = ?(block) match {
    case a: A => Some(a)
    case _ => None
  }

like so:

??(a.b.c.d) match {
    case Some(result) => // do more things with result
    case None => // handle "null" case
  }

这篇关于Groovy 的安全取消引用运算符 (?.) 的最佳 Scala 模仿?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆