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

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

问题描述

我想知道什么是最好的Scala模仿Groovy的安全解引用运算符(? ),或至少有一些接近的替代品是?



> Daniel Spiewak 的博客,但希望将其打开到StackOverFlow ...



为了大家的时间,这里是Daniel的初始回应,我的柜台和他的第二个回应:


@Antony



其实,我先看了一个
。或者说,我试图用
复制来自Ruby land的Ragenwald和and
operator。问题
是,如果没有代理服务器
,有点难。考虑下面的
表达式(使用Ruby的
andand,但它与
Groovy的运算符相同):
$ b

test.andand() .doSomething()



我可以从Any =>创建隐式转换
,实现
andand()方法的某种类型,但这就是
魔法停止。无论
值是否为null,
doSomething()方法仍将执行
。由于它必须以类型安全的方式在
上执行某个目标,所以
需要实现字节码代理的
,这将是
片状和奇怪的
注解,final方法,
构造函数等)。

更好的选择是回到
作为灵感来源
和$以及Groovy的安全
解引用运算符:monadic map
操作。以下是一些Scala
语法,它使用Option来实现
模式:



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



val length = something.map(_。length)



在此之后, length 或者是
Some(str.length)(其中str是包含的
String对象)
选项)或无。这正是safe-dereferencing操作符
的工作原理,
除了它使用null而不是
类型安全的monad外。



如上所述,我们可以定义
从某种类型的
T => Option [T]的隐式转换,然后映射到
时尚中,但某些类型已经有
映射定义,所以它不会非常有用。或者,我可以
实现与map类似的功能,但
具有单独的名称,但是它以
的任何方式实现,它将依赖
高阶函数而不是
简单的链式呼叫。它似乎是
只是静态类型
语言的本质(如果任何人有一个方法
这个,请随时纠正我)。

丹尼尔Spiewak星期一,2008年7月7日在
1:42 pm

我的第二个问题:

p>


感谢Daniel
回应。我想我错过了! I
认为我明白你的
建议,但假设你没有
控制来源,那么
这样的东西是怎么回事?




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




假设它是一个java bean,并且您不能去
并将返回值更改为
什么东西[T] - 我们能在那里做什么?

Antony Stubbs 2009年7月21日星期二
at 8:07 pm oh gosh - ok on re -read
这就是你建议
从T到
的隐式转换的选项[T]是吧?但是,你仍然会
能够像
那样链接在一起吗?你仍然需要地图吗?
hmm ...。




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






Antony Stubbs 2009年7月21日星期二下午8:10

他的第二个回应:


@Antony



我们真的不能在
中做任何事情吗?.getContactPerson,
等......即使假设这是有效的
Scala语法,我们仍然需要一些
的方式来阻止
链中的稍后调用。这是不可能的,如果我们
不使用函数值。因此,
类似map这样的东西实际上是唯一的
选项。



隐式转换为Option
并不会坏,但通过使事物隐含
,我们绕过了类型系统保护中的某些

做这种事情的最好方法是
,用于在

中用于理解。我们可以做map和
flatMap,但用
的神奇语法更好:



 用于{
c< - 公司
人< - c.getContactPerson
details< - person.getContactDetails
address< - details.getAddress
} yield address.getCity




Daniel Spiewak 2009年7月21日星期二下午9:28

Ps如果丹尼尔在他的博客上发表他的原始答案作为答案,我将编辑该问题以便为了系统而删除它们。



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

使用这个小片段,您可以安全地解除引用并且代码本身相当简洁:

  val a =?(bcde)

a == null如果b或bc或bcd或bcde为空,则a == bcde



I认为当你使用像Scala这样的语言时,安全解引用运算符的价值会降低,这种语言具有像名称和含义这样的功能。



ps:我修改上面的代码根据下面的注释之一来处理NullPointerException是实际在被调用函数内抛出的
的情况。


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

  def ?? [A](block:=> A):Option [A] =?(block)match {
case a:A =>一些(a)
案例_ =>无
}

如此:

  ??(abcd)match {
case Some(result)=> //用结果做更多​​事情
case None => //处理空情况
}


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
  }

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

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