加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 磁铁模式和超载方法

发布时间:2020-12-16 09:34:55 所属栏目:安全 来源:网络整理
导读:Scala如何解决非重载和重载方法的“磁图模式”的隐式转换。 假设有一个特征应用(“磁铁图案”的变体))如下实现。 trait Apply[A] { def apply(): A}object Apply { implicit def fromLazyVal[A](v: = A): Apply[A] = new Apply[A] { def apply(): A = v }}
Scala如何解决非重载和重载方法的“磁图模式”的隐式转换。

假设有一个特征应用(“磁铁图案”的变体))如下实现。

trait Apply[A] {
 def apply(): A
}
object Apply {
  implicit def fromLazyVal[A](v: => A): Apply[A] = new Apply[A] {
    def apply(): A = v
  }
}

现在我们创建一个具有单个应用的特征Foo,使用Apply的一个实例,所以我们可以传递任意类型A的任何值,因为从A =>的隐式转换应用[A]。

trait Foo[A] {
  def apply(a: Apply[A]): A = a()
}

我们可以使用REPL和this workaround to de-sugar Scala code确保它的运行正常。

scala> val foo = new Foo[String]{}
foo: Foo[String] = $anon$1@3a248e6a

scala> showCode(reify { foo { "foo" } }.tree)
res9: String =    
$line21$read.foo.apply(
  $read.INSTANCE.Apply.fromLazyVal("foo")
)

这很有用,但是假设我们将一个复杂的表达式(with;)传递给apply方法。

scala> val foo = new Foo[Int]{}
foo: Foo[Int] = $anon$1@5645b124

scala> var i = 0
i: Int = 0

scala> showCode(reify { foo { i = i + 1; i } }.tree)
res10: String =
$line23$read.foo.apply({
  $line24$read.`i_=`($line24$read.i.+(1));
  $read.INSTANCE.Apply.fromLazyVal($line24$read.i)
})

我们可以看到,隐式转换只适用于复合表达式(即i)的最后部分,而不是整个表达式。所以,i = i 1在我们把它传递给一个应用方法的时刻被严格评估,这不是我们一直期待的。

好(或坏)消息。我们可以使scalac在隐式转换中使用整个表达式。所以i = i 1将按预期的方式进行懒惰评估。我们(surprize,surprize!)我们添加一个重载方法Foo.apply,它采用任何类型,但不适用。

trait Foo[A] {
  def apply(a: Apply[A]): A = a()
  def apply(s: Symbol): Foo[A] = this
}

接着。

scala> var i = 0
i: Int = 0

scala> val foo = new Foo[Int]{}
foo: Foo[Int] = $anon$1@3ff00018

scala> showCode(reify { foo { i = i + 1; i } }.tree)
res11: String =
$line28$read.foo.apply($read.INSTANCE.Apply.fromLazyVal({
  $line27$read.`i_=`($line27$read.i.+(1));
  $line27$read.i
}))

我们可以看到,整个表达式i = i 1;我按预期在隐含的转换下做了。

所以我的问题是为什么呢?为什么应用隐式转换的范围取决于类中是否存在重载方法的事实。

解决方法

现在,这是一个棘手的事情。而且实际上真的很棒,我不知道“懒惰隐含不包括全部块”的“解决方法”问题。感谢那!

发生什么与预期类型相关,以及它们如何影响类型推断工作,隐式转换和重载。

类型推断和预期类型

首先,我们必须知道Scala中的类型推断是双向的。大多数推论是从下到上(给定一个:Int和b:Int,推断b:Int),但有些事情是自上而下的。例如,推测lambda的参数类型是自上而下的:

def foo(f: Int => Int): Int = f(42)
foo(x => x + 1)

在第二行,在将foo解析为def foo(f:Int => Int):Int之后,类型参与者可以告诉x必须是Int类型。它在类型检查lambda本身之前这样做。它将类型信息从函数应用程序传播到lambda,这是一个参数。

自上而下的推论基本上依赖于预期类型的??概念。当类型检查程序的AST节点时,类型检查器不会空手动。它从“上面”(在这种情况下是功能应用程序节点)接收到一个预期类型。当类型检查lambda x =>在上述示例中,x 1,期望类型为Int => Int,因为我们知道foo有什么参数类型。这推动了类型推断,推断参数x的Int,这反过来允许类型检查x 1。

预期类型向下传播某些构造,例如块({})以及ifs和match的分支。因此,你也可以用foo来调用foo

foo({
  val y = 1
  x => x + y
})

而typechecker仍然能够推断出x:Int。这是因为,当类型检查块{…}时,预期类型Int => Int被传递到对最后一个表达式的类型检验,即x => x y。

隐式转换和预期类型

现在,我们必须在组合中引入隐式转换。当类型检查节点产生类型T的值时,该节点的预期类型为U,其中T U(我可能在这里简化了一些事情,但是要点仍然是真的)。这就是为什么你的第一个例子不起作用。让我们仔细看一下: 为假,类型检查器查找隐式t>

trait Foo[A] {
  def apply(a: Apply[A]): A = a()
}

val foo = new Foo[Int] {}
foo({
  i = i + 1
  i
})

当调用foo.apply时,参数(即块)的预期类型是Apply [Int](A已经被实例化为Int)。我们可以这样写“这个typechecker”状态“

{
  i = i + 1
  i
}: Apply[Int]

这个预期类型被传递给块的最后一个表达式,它给出:

{
  i = i + 1
  (i: Apply[Int])
}

在这一点上,由于i:Int和期望类型是Apply [Int],所以typechecker会发现隐式转换:

{
  i = i + 1
  fromLazyVal[Int](i)
}

这只会引起我的迷茫。

重载和预期类型

好吧,时间在那里抛出超载!当typechecker看到一个应用程序的重载方法,它有更多的麻烦决定一个预期的类型。我们可以看到,通过以下示例:

object Foo {
  def apply(f: Int => Int): Int = f(42)
  def apply(f: String => String): String = f("hello")
}

Foo(x => x + 1)

得到:

error: missing parameter type
              Foo(x => x + 1)
                  ^

在这种情况下,类型检查器找出预期类型的??失败将导致不推断参数类型。

如果我们把你的“解决方案”给你的问题,我们有不同的后果:

trait Foo[A] {
  def apply(a: Apply[A]): A = a()
  def apply(s: Symbol): Foo[A] = this
}

val foo = new Foo[Int] {}
foo({
  i = i + 1
  i
})

现在当类型检查块时,类型检查器没有预期的类型可以使用。因此,它将在没有表达式的情况下键入最后一个表达式,并最终将整个块作为Int进行类型检查:

{
  i = i + 1
  i
}: Int

只有现在,已经有类型检查的参数,它是否尝试解决重载。因为没有任何重载直接符合,所以它尝试将int的隐式转换应用于Apply [Int]或Symbol。它发现fromLazyVal [Int],它适用于整个参数。它不会把它推到块内,给出:

fromLazyVal({
  i = i + 1
  i
}): Apply[Int]

在这种情况下,整个程序段被放宽了。

最后解释。总而言之,主要的区别是当对块进行类型检查时,存在与不存在预期类型。使用预期的类型,隐式转换被尽可能多地推下来。没有预期类型,隐式转换在整个参数即整个块上应用于后验。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读