理解 Scala 中的隐式 [英] Understanding implicit in Scala

查看:35
本文介绍了理解 Scala 中的隐式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在学习 Scala playframework 教程时遇到了这段让我感到困惑的代码片段:

def newTask = Action { 隐式请求 =>taskForm.bindFromRequest.fold(错误 =>BadRequest(views.html.index(Task.all(), errors)),标签 =>{任务.创建(标签)重定向(routes.Application.tasks())})}

所以我决定进行调查并发现了这篇文章.>

我还是不明白.

这有什么区别:

implicit def double2Int(d : Double) : Int = d.toInt

def double2IntNonImplicit(d : Double) : Int = d.toInt

除了显而易见的事实之外,它们具有不同的方法名称.

什么时候应该使用 implicit 以及为什么?

解决方案

我将在下面解释隐式的主要用例,但更多细节参见 Scala 编程的相关章节.

隐式参数

方法的最终参数列表可以标记为implicit,这意味着这些值将从调用它们的上下文中获取.如果作用域中没有正确类型的隐式值,它将不会编译.由于隐式值必须解析为单个值并避免冲突,因此最好使类型特定于其目的,例如不需要您的方法来查找隐式 Int

示例:

//可能在一个库中类前缀(val前缀:字符串)def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s//那么可能在你的应用程序中隐式 val myImplicitPrefixer = new Prefixer("***")addPrefix("abc")//返回 "***abc"

隐式转化

当编译器为上下文找到错误类型的表达式时,它将寻找一个隐式的 Function 类型值,以允许它进行类型检查.因此,如果需要 A 并且找到 B,它将查找 B => 类型的隐式值.A 范围内(它还检查其他一些地方,如 BA 伴随对象,如果它们存在).由于 defs 可以eta-expanded"到 Function 对象中,隐式 def xyz(arg: B): A 也可以.

因此,您的方法之间的区别在于,当找到 DoubleIntimplicit 的方法代码> 是必需的.

implicit def doubleToInt(d: Double) = d.toIntval x: 整数 = 42.0

将与

相同

def doubleToInt(d: Double) = d.toIntval x: Int = doubleToInt(42.0)

在第二个中,我们手动插入了转换;首先,编译器会自动执行相同的操作.由于左侧的类型注释,需要进行转换.

<小时>

关于你的第一个 Play 片段:

操作在 此页面上从 Play 文档中进行了解释(另请参阅API 文档).您正在使用

apply(block: (Request[AnyContent]) ⇒ Result): Action[AnyContent]

Action 对象上(它是同名 trait 的伴侣).

所以我们需要提供一个Function作为参数,它可以写成如下形式的文字

request =>...

在函数字面量中,=> 之前的部分是一个值声明,如果需要,可以标记为 implicit,就像在任何其他 中一样>val 声明.在这里,request 不必被标记为implicit以便进行类型检查,但这样做后它可以作为函数中可能需要它的任何方法的隐式值(当然,它也可以显式使用).在这种特殊情况下,这是因为 Form 类需要一个隐式的 Request 参数.

I was making my way through the Scala playframework tutorial and I came across this snippet of code which had me puzzled:

def newTask = Action { implicit request =>
taskForm.bindFromRequest.fold(
        errors => BadRequest(views.html.index(Task.all(), errors)),
        label => {
          Task.create(label)
          Redirect(routes.Application.tasks())
        } 
  )
}

So I decided to investigate and came across this post.

I still don't get it.

What is the difference between this:

implicit def double2Int(d : Double) : Int = d.toInt

and

def double2IntNonImplicit(d : Double) : Int = d.toInt

other than the obvious fact they have different method names.

When should I use implicit and why?

解决方案

I'll explain the main use cases of implicits below, but for more detail see the relevant chapter of Programming in Scala.

Implicit parameters

The final parameter list on a method can be marked implicit, which means the values will be taken from the context in which they are called. If there is no implicit value of the right type in scope, it will not compile. Since the implicit value must resolve to a single value and to avoid clashes, it's a good idea to make the type specific to its purpose, e.g. don't require your methods to find an implicit Int!

example:

  // probably in a library
class Prefixer(val prefix: String)
def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s

  // then probably in your application
implicit val myImplicitPrefixer = new Prefixer("***")
addPrefix("abc")  // returns "***abc"

Implicit conversions

When the compiler finds an expression of the wrong type for the context, it will look for an implicit Function value of a type that will allow it to typecheck. So if an A is required and it finds a B, it will look for an implicit value of type B => A in scope (it also checks some other places like in the B and A companion objects, if they exist). Since defs can be "eta-expanded" into Function objects, an implicit def xyz(arg: B): A will do as well.

So the difference between your methods is that the one marked implicit will be inserted for you by the compiler when a Double is found but an Int is required.

implicit def doubleToInt(d: Double) = d.toInt
val x: Int = 42.0

will work the same as

def doubleToInt(d: Double) = d.toInt
val x: Int = doubleToInt(42.0)

In the second we've inserted the conversion manually; in the first the compiler did the same automatically. The conversion is required because of the type annotation on the left hand side.


Regarding your first snippet from Play:

Actions are explained on this page from the Play documentation (see also API docs). You are using

apply(block: (Request[AnyContent]) ⇒ Result): Action[AnyContent]

on the Action object (which is the companion to the trait of the same name).

So we need to supply a Function as the argument, which can be written as a literal in the form

request => ...

In a function literal, the part before the => is a value declaration, and can be marked implicit if you want, just like in any other val declaration. Here, request doesn't have to be marked implicit for this to type check, but by doing so it will be available as an implicit value for any methods that might need it within the function (and of course, it can be used explicitly as well). In this particular case, this has been done because the bindFromRequest method on the Form class requires an implicit Request argument.

这篇关于理解 Scala 中的隐式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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