磁铁模式和重载方法

2024-01-03

对于非重载和重载方法,Scala 解决“磁铁模式”隐式转换的方式存在显着差异。

假设有一个特质Apply(“磁铁图案”的变体)实施如下。

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举个例子Apply所以我们可以向它传递任意类型的任何值A因为有一个隐式转换A => Apply[A].

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

我们可以使用 REPL 确保它按预期工作,并且这个解决方法可以减少 Scala 代码的糖分 https://gist.github.com/mpilquist/4ed3196e9ee4543c1847.

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 method.

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在我们将其传递给apply方法,这不是我们所期望的。

好(或坏)消息。我们可以做scalac在隐式转换中使用整个表达式。所以i = i + 1将按预期进行延迟评估。为此,我们(惊讶,惊讶!)我们添加了一个重载方法Foo.apply可以接受任何类型,但不是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; i正如预期的那样,在隐式转换下实现了它。

所以我的问题是为什么会这样?为什么隐式转换的范围取决于类中是否有重载方法。


现在,这是一个棘手的问题。它实际上非常棒,我不知道“惰性隐式不覆盖完整块”问题的“解决方法”。感谢那!

发生的事情与预期类型,以及它们如何影响类型推断工作、隐式转换和重载。

类型推断和预期类型

首先,我们必须知道 Scala 中的类型推断是双向的。大多数推理都是自下而上进行的(给定a: Int and b: Int, infer a + b: Int),但有些事情是自上而下的。例如,推断 lambda 的参数类型是自上而下的:

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

在第二行中,解决后foo to be def foo(f: Int => Int): Int,类型推断器可以知道x必须是类型Int。确实如此before对 lambda 本身进行类型检查。它将类型信息从函数应用程序向下传播到 lambda(参数)。

自上而下的推理基本上依赖于以下概念预期类型。当对程序的 AST 节点进行类型检查时,类型检查器不会空手启动。它从“上方”(在本例中为函数应用程序节点)接收预期类型。对 lambda 进行类型检查时x => x + 1在上面的例子中,预期的类型是Int => Int,因为我们知道期望的参数类型foo。这将类型推断驱动为推断Int对于参数x,这又允许类型检查x + 1.

预期的类型会沿着某些构造传播,例如块({})和分支ifs and matches.因此,您也可以调用foo with

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

并且类型检查器仍然能够推断x: Int。那是因为,在类型检查块时{ ... }, 预期类型Int => Int被传递到最后一个表达式的类型检查,即x => x + y.

隐式转换和预期类型

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

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)。我们可以像这样“写”这个类型检查器“状态”:

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

这个预期的类型是传承下来到块的最后一个表达式,它给出:

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

至此,自从i: Int预期类型是Apply[Int],类型检查器找到隐式转换:

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

这只会导致i变得懒惰。

重载和预期类型

好啦,是时候投入重载了!当类型检查器看到重载方法的应用时,它在决定预期类型时会遇到更多困难。我们可以通过以下示例看到这一点:

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

Foo(x => x + 1)

gives:

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] or Symbol。它发现fromLazyVal[Int],它适用于到整个论证。它不再将其推入块内,给出:

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

在这种情况下,整个块都是惰性的。

解释到此结束。总而言之,主要区别在于对块进行类型检查时是否存在预期类型。对于预期的类型,隐式转换会尽可能向下推,直到i。如果没有预期的类型,则隐式转换将后验应用于整个参数(即整个块)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

磁铁模式和重载方法 的相关文章

  • 如何识别远程参与者?

    我有一个远程参与者 客户端 它正在向另一个远程参与者 服务器 注册 然后注销 使用关闭挂钩 然而 虽然服务器接收到注销 但实际sender财产是一个不同的 Channel 对象 所以在我的服务器日志中我有 Registered new cl
  • Scala SBT 版本依赖性二进制兼容性错误 scala-xml

    我有一个在 GitHub 上托管的项目 我使用 scala steward 来保持我的插件和依赖项最新 这在一段时间内有效 但现在使用此类自动更新却变成了一场噩梦 事情是这样的 在我的plugins sbt中 我依赖于scoverage 它
  • 对列表中的相邻元素进行分组

    假设我想编写一个函数来执行此操作 输入 1 1 3 3 4 2 2 5 6 6 输出 1 1 3 3 4 2 2 5 6 6 它将相同的相邻元素分组 这个方法的名称应该是什么 此操作有标准名称吗 In 1 1 3 3 4 2 2 5 6 6
  • Akka/Scala:映射 Future 与 pipelineTo

    In Akka参与者 在发送一个Future结果给另一个演员 A 映射Future发挥作用tell结果给演员 B 定义一个onSuccess未来的回调 其中tell结果给演员 C 管道Future结果给演员pipeTo 其中一些选项已在上一
  • 手动排除sbt中的一些测试类

    我通常在 CI 中执行以下命令 清理更新编译测试发布 但是 我想从 sbt 命令行中排除 1 个 或几个 测试类 我怎样才能做到这一点 我不想更改我的代码以使用忽略等 两种可能的选择 test only See http www scala
  • 用 HashMap[Int, Vector[Int]] (Scala) 表示图(邻接列表)?

    我想知道如何 如果可能的话 我可以通过以下方式制作 可变 图的邻接列表表示HashMap Int Vector Int HashMap当然是可变的 目前我将其设置为HashMap Int ArrayBuffer Int 但我可以更改 Arr
  • Scala 中的 Apply 和 lambda

    我有下面的代码 scala gt val builder new StringBuilder foo bar baz builder StringBuilder foo bar baz scala gt 0 until 5 foreach
  • Scala 模式匹配打印漂亮

    是否有可能以某种方式编组部分函数 假设它总是只包含一种情况 进入某物人类可读的 假设我们有 Any 类型的集合 消息 List Any 以及使用模式匹配块定义的 PartialFuntion Any T 的数量 case object R1
  • 如何在 akka actor 中测试公共方法?

    我有一个 akka 演员 class MyActor extends Actor def recieve def getCount id String Int do a lot of stuff proccess id do more st
  • Scala 中缺少多重集吗?

    我正在尝试 Scala 中的 Facebook Hacker Cup 2013 资格赛问题 对于第三个问题 我觉得需要一个有序的 Multiset 但在 scala 的 2 10 集合中找不到一个 scala 的集合中是否缺少此数据结构 会
  • Scala REPL / SBT Console 是否有配置文件?

    我一直在尝试找到某种点文件来放入 Scala REPL 设置和自定义函数 我特别有兴趣传递它的标志 例如 Dscala color 启用语法突出显示 以及覆盖设置 如结果字符串截断 scala gt power scala gt vals
  • 如何向数据框spark添加标题和列?

    我有一个数据框 我想在其中添加标题和第一列 手动 这是数据框 import org apache spark sql SparkSession val spark SparkSession builder master local appN
  • 创建自定义 scala 集合,其中映射默认返回自定义集合?

    特质TraversableLike A Repr 允许人们在其中进行收藏some函数将返回一个Repr 而其他人则继续返回类型参数That在功能上 有没有办法定义一个CustomCollection A 其中函数如map 其他的默认That
  • Scalaz 拆箱标记类型不会自动拆箱

    Reading http eed3si9n com learning scalaz Tagged type html http eed3si9n com learning scalaz Tagged type html并尝试示例代码 imp
  • 按元素聚合数组

    Spark scala 相当新 我想知道是否有一种简单的方法以按列方式聚合 Array Double 这是一个例子 c1 c2 c3 1 1 1 0 1 0 3 4 1 2 1 0 0 0 4 3 2 1 0 0 0 0 0 0 2 3 1
  • 对 Spark 数据集中的数字字符串进行排序

    假设我有以下内容Dataset productCode amount XX 13 300 XX 1 250 XX 2 410 XX 9 50 XX 10 35 XX 100 870 Where productCode is of Strin
  • 使用 org.apache.hadoop/* 依赖项离线编译 sbt 时遇到的问题

    使用依赖于 org apache hadoop 包的 sbt 进行离线编译时遇到很多麻烦 一个简单的build sbt name Test version 1 0 scalaVersion 2 10 4 libraryDependencie
  • Scala 隐式转换范围问题

    采取这个代码 class Register var value Int 0 def getZeroFlag Boolean value 0x80 0 object Register implicit def reg2int r Regist
  • 在 Scala 中反转地图的优雅方法

    目前正在学习Scala 需要反转Map 来进行一些反转值 gt 键查找 我一直在寻找一种简单的方法来做到这一点 但只想到了 Map origMap map kvp gt kvp 2 gt kvp 1 有人有更优雅的方法吗 假设值是唯一的 则
  • Spark scala:大量列上的简单 UDF 会导致性能下降

    我有一个包含 1 亿行和约 10 000 列的数据框 这些列有两种类型 标准 C i 和动态 X i 这个dataframe是经过一些处理后得到的 性能很快 现在只剩下2步了 Goal 需要使用 C i 列的相同子集对每个 X i 执行特定

随机推荐

  • 创建或附加到字典中的列表 - 可以缩短吗?

    可以使用 itertools 和 set 缩短此 Python 代码并仍然可读吗 result for widget type app in widgets if widget type not in result result widge
  • 如何在某种条件下跳过第一个活动

    我正在将一组应用程序构建为类似于 MS Office 的包 这里每个应用程序都有自己的启动器 并且可以从家庭应用程序内部启动 每个应用程序都有一个登录页面 当应用程序从 android 启动器启动时 我需要显示登录页面 而从家庭应用程序启动
  • 如何以编程方式强制停止应用程序 - Android

    我想从我的 Android 应用程序中强制停止应用程序 而不是通过 Man apps gt force stop 手动执行 如何实现这一目标 Process killProcess Process myPid
  • 在 CSS 缩放画布中获取正确的鼠标位置

    我一直在尝试采用几个简单的 MooTools 操作的逻辑并将其转换为与 jQuery 一起使用 我想要的逻辑是这样的jsfiddle http jsfiddle net rQkSF 它允许通过 css 调整大小来获得画布元素上准确的鼠标位置
  • 如何绘制宽度不等的直方图而不从原始数据计算?

    Matplotlib 的hist http matplotlib org api pyplot api html matplotlib pyplot hist说 计算并绘制 x 的直方图 我想做一个情节without首先计算任何东西 我有箱
  • iOS10 中的动画导航栏 barTintColor 更改不起作用

    我升级到 XCode 8 0 iOS 10 现在导航栏的颜色变化动画不再起作用 它直接改变颜色 没有任何动画 UIView animateWithDuration 0 2 animations self navigationControll
  • 如何在android中获取设备的最大宽度和高度

    您好 我在为 Android 应用程序创建自定义视图时遇到问题 如果允许 我的自定义视图想要使用最大屏幕宽度 我找不到任何方法来检索该值 有人能指出我正确的方法吗 Try mWinMgr WindowManager context getS
  • a 的 b 次方,不带 (a**b),Python

    正在努力完成一项要求我在没有此运算符的情况下编写 a b 的练习 尝试自己写一些东西 但没有得到正确的结果 我得到的不是一个值 而是两个 两者都不正确 看起来计数器并没有真正增加 我可以寻求帮助吗 谢谢 def powerof base e
  • 在 Xcode ≥ 7.3 中处理私有框架

    在 Xcode 7 3 iOS 9 3 中 Apple 删除了所有私有框架 https developer apple com library ios releasenotes DeveloperTools RN Xcode Chapter
  • 控制台应用程序中的倒计时器[重复]

    这个问题在这里已经有答案了 我有一个控制台应用程序 我想创建一个倒计时器 这是我尝试过的 static void Main string args for int a 10 a gt 0 a Console Write Generating
  • 如何使用 VBScript 比较数组与数组?

    我想检查我的文件中的数据是否存在于我拥有的数组数据中 如果退出则返回1 退出则返回0 我的文件里面是这样的 2j2H4F6d9d0d3hdfasgt y7 但我剪掉了最后两行 我的数组数据是这样的 2w fr 5k 2j 0w 我想检查我的
  • 编辑我的 ReactJS Webpack 配置文件时出现“语法错误:意外的严格模式保留字”

    我试图按照此问题答案中的说明来修复无效主机错误 远程连接到 webpack dev server 时收到 无效主机标头 消息 https stackoverflow com questions 43619644 i am getting a
  • 谷歌地图:标记边界

    我正在尝试在 Google 地图上将照片显示为标记 通过将标记重新定义为图像 这没有问题 但我想添加某种边框或阴影以使它们更好地突出 像谷歌照片这样的东西是最好的 但我想这太复杂了 如果可能的话 一个简单的 1px 实心边框就可以了 一种选
  • JQuery:.val() 不适用于文本区域

    我正在尝试读取文本区域的内容 但是 val 不适用于我的文本区域 如果有人为我提供解决方案 我会很高兴 这是我的代码 HTML
  • 如何自定义/设计 UIPopoverController

    我正在开发 iPad 应用程序 并且正在使用 UIPopoverControllers 我正处于应用程序需要品牌化和样式化的部分 我想知道如何更改 UIPopoverController 的颜色 色调 标准是深蓝色 但它需要是其他颜色 这可
  • 在输出到控制台之前在 Spark Structured Streaming 上处理数据

    我会尽量保持简单 我定期从 kafka 生产者读取一些数据 并使用 Spark 结构化流输出以下内容 我有这样输出的数据 window timestamp Online Emp Available Emp
  • 何时在 javascript OO 中使用它?

    在 Javascript OO 中 我什么时候应该使用this关键词 另外 如果我想从同一个类的另一个方法调用一个类的方法 我应该使用this或者只是函数的名称 例如 这是正确的吗 function Foo this bar functio
  • 角度材质数据表排序不起作用/未显示箭头

    我正在尝试对我的角度材料数据表进行排序 它正确显示数据 但排序不起作用 它甚至没有在标题旁边显示小箭头来指示它正在排序 这是我的组件 import Component OnInit ViewChild AfterViewInit from
  • 带有多条消息的连续滚动股票/选取框 - 需要 JS 插件吗?

    我想要实现的一个很好的例子是股票行情效应https aboutface com https aboutface com 根据我不久前看到的另一个例子 我想出了这个 但正如您所看到的 消息被裁剪 并且您在屏幕上看不到第二条消息 滚动 可见区域
  • 磁铁模式和重载方法

    对于非重载和重载方法 Scala 解决 磁铁模式 隐式转换的方式存在显着差异 假设有一个特质Apply 磁铁图案 的变体 实施如下 trait Apply A def apply A object Apply implicit def fr