隐式使用 Scala 实现类型相等

2023-11-26

我一直在阅读一些有关 Scala 类型级编程的内容。主要是 Apocalisp 博客,还有 Alexander Lehmann 的 YouTube 演讲。

我有点坚持我认为可能非常基本的事情,即使用隐式比较两种类型,如下所示:

implicitly[Int =:= Int]

马克在 Apocalisp 博客上说道:

这对于捕获范围内且类型为 T 的隐式值很有用。

我知道如何使这项工作起作用,但我真的不知道它为什么起作用,所以不想继续前进。

在上面的例子中,作用域中是否有一个隐式的“Int”类型,它“隐式”地从以太中获取,允许代码编译?它与 'function1' 返回类型有何关系?

res0: =:=[Int,Int] = <function1>

另外,这个隐式从何而来?就我的特质“Foo”而言,为什么

implicitly[Foo =:= Foo] 

编译?在这种情况下,“Foo”隐式从何而来?

如果这是一个非常愚蠢的问题,请提前道歉,并感谢您的帮助!


X =:= Y只是类型的语法糖(中缀表示法)=:=[X, Y].

所以当你这样做时implicitly[Y =:= Y],您只需查找类型的隐式值=:=[X, Y]. =:=是定义在中的通用特征Predef.

Also, =:=是一个有效的类型名称,因为类型名称(就像任何标识符一样)可以包含特殊字符。

从现在起,我们重新命名吧=:= as IsSameType并删除中缀符号,以使我们的代码看起来更简单,更不那么神奇。 这给了我们implicitly[IsSameType[X,Y]]

以下是该类型定义方式的简化版本:

sealed abstract class IsSameType[X, Y]
object IsSameType {
   implicit def tpEquals[A] = new IsSameType[A, A]{}
}

注意如何tpEquals提供隐含值IsSameType[A, A]对于任何类型A。 换句话说,它提供了一个隐含的值IsSameType[X, Y]当且仅当X and Y是同一类型。 所以implicitly[IsSameType[Foo, Foo]]编译得很好。 但implicitly[IsSameType[Int, String]]不会,因为类型范围没有隐式IsSameType[Int, String], 鉴于tpEquals在这里不适用。

因此,通过这个非常简单的构造,我们可以静态地检查某些类型X与其他类型相同Y.


现在这里是一个示例,说明它的用途。假设我想定义一个 Pair 类型(忽略它已经存在于标准库中的事实):

case class Pair[X,Y]( x: X, y: Y ) {
  def swap: Pair[Y,X] = Pair( y, x )
}

Pair是用它的 2 个元素的类型参数化的,这两个元素可以是任何东西,最重要的是不相关。 现在如果我想定义一个方法怎么办toList将这对转换为 2 元素列表? 这种方法只有在以下情况下才真正有意义X and Y是一样的,否则我将被迫返回List[Any]。 我当然不想改变的定义Pair to Pair[T]( x: T, y: T )因为我真的希望能够拥有成对的异构类型。 毕竟只是在打电话的时候toList我需要 X == Y。所有其他方法(例如swap) 应该可以在任何类型的异质对上调用。 所以最后我真的想静态地确保 X == Y,但只有在调用时toList,在这种情况下,可以并且一致地返回List[X] (or a List[Y],这是同一件事):

case class Pair[X,Y]( x: X, y: Y ) {
  def swap: Pair[Y,X] = Pair( y, x )
  def toList( implicit evidence: IsSameType[X, Y] ): List[Y] = ???
}

但实际执行起来还存在严重问题toList。如果我尝试编写明显的实现,则无法编译:

def toList( implicit evidence: IsSameType[X, Y] ): List[Y] = List[Y]( x, y )

编译器会抱怨x不属于类型Y。确实,X and Y就编译器而言,它们仍然是不同的类型。 只有通过仔细构造,我们才能静态地确定 X == Y (即,事实是toList接受类型的隐式值IsSameType[X, Y],并且它们是由方法提供的tpEquals仅当 X == Y 时)。 但编译器肯定不会破译这个精明的结构来得出 X == Y 的结论。

为了解决这种情况,我们可以做的是提供从 X 到 Y 的隐式转换,前提是我们知道 X == Y(或者换句话说,我们有一个实例)IsSameType[X, Y]在适用范围)。

// A simple cast will do, given that we statically know that X == Y
implicit def sameTypeConvert[X,Y]( x: X )( implicit evidence: IsSameType[X, Y] ): Y = x.asInstanceOf[Y]

现在,我们的实施toList最终编译良好:x将简单地转换为Y通过隐式转换sameTypeConvert.

作为最后的调整,我们可以进一步简化事情:假设我们采用隐式值(evidence)已经作为参数, 为什么不让这个值实现转换?像这样:

sealed abstract class IsSameType[X, Y] extends (X => Y) {
  def apply( x: X ): Y = x.asInstanceOf[Y]
}
object IsSameType {
   implicit def tpEquals[A] = new IsSameType[A, A]{}
}    

然后我们可以删除该方法sameTypeConvert,因为隐式转换现在由IsSameType实例本身。 现在IsSameType有双重目的:静态地确保 X == Y,并且(如果是的话)提供隐式转换,实际上允许我们处理X作为实例Y.

我们现在基本上重新实现了类型=:=定义如下Predef


UPDATE:从评论中可以看出,使用asInstanceOf困扰着人们(尽管它实际上只是一个实现细节,并且没有用户IsSameType需要进行演员表)。事实证明,即使在实施过程中也很容易摆脱它。看哪:

sealed abstract class IsSameType[X, Y] extends (X => Y) {
  def apply(x: X): Y
}
object IsSameType {
  implicit def tpEquals[A] = new IsSameType[A, A]{
    def apply(x: A): A = x
  }
}

基本上,我们只需离开apply抽象,并且只在中正确实现它tpEquals我们(和编译器)知道传递的参数和返回值确实具有相同的类型。因此不需要任何演员阵容。确实如此。

请注意,最后,相同的转换仍然存在于生成的字节码中,但现在源代码中不存在,并且从编译器的角度来看可以证明是正确的。尽管我们did引入一个附加的(匿名)类(因此从抽象类到具体类的附加间接),它should在任何像样的虚拟机上运行得一样快,因为我们处于“单态方法调度”的简单情况(如果您对虚拟机的内部工作感兴趣,请查找它)。尽管它可能仍然使虚拟机更难内联调用apply(运行时虚拟机优化是一种黑术,很难做出明确的说法)。

最后一点,我必须强调,如果代码被证明是正确的,那么无论如何在代码中进行强制转换确实没什么大不了的。毕竟,标准库本身直到最近都有同样的强制转换(现在整个实现已经过修改,使其看起来更强大,但在其他地方仍然包含强制转换)。如果它对于标准库来说足够好,那么对我来说也足够好。

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

隐式使用 Scala 实现类型相等 的相关文章

  • 为什么 C# 中 bool 数据类型的大小不是只有 1 位?

    我刚刚学习 C 并深入研究数据类型 为什么不是一个bool数据类型大小为 1 位 看起来它只能保存两个值之一 true 或 false 那么这不是只占用 1 位空间来表示该值吗 是因为值的最小 可寻址 大小是一个字节 8 位 吗 这个帖子
  • 在 scala 中混合类型参数和抽象类型

    我正在尝试使用的答案前面的问题 https stackoverflow com questions 2059601 circular type parameters definition in scala实现一个小型图形库 这个想法是将图视
  • 这种奇怪的 Scala 内存泄漏的原因是什么? [复制]

    这个问题在这里已经有答案了 即使有 7G 的堆空间 这也会耗尽内存 import scala collection mutable Set class Foo val anEmptySet Set Int Set def bar ints
  • 使一个 sbt 配置依赖于另一个配置

    sbt 文档显示了如何仅在项目之间声明依赖关系的示例 但我确信有方法可以声明一个配置依赖于另一个配置 就像测试配置使用编译配置中的类路径一样 如何声明我自己的配置 以便它依赖于编译配置生成的类路径 我更仔细地研究了建议的解决方案 然后又出现
  • 如何使用 *non-case* 类通过 Scala 从 JSON 反序列化?

    我正在编写一个 Scala 应用程序 需要序列化和反序列化 JSON 某些 JSON 对象具有超过 22 个字段 因此我无法使用案例类 并且也无法更改格式 我找到的所有 Scala JSON 库都只能 轻松地 与案例类一起使用 而不是与普通
  • Spark DataFrame 不尊重架构并将所有内容视为字符串

    我面临着一个多年来一直无法克服的问题 我使用的是 Spark 1 4 和 Scala 2 10 我现在无法升级 大型分布式基础设施 我有一个包含几百列的文件 其中只有 2 列是字符串 其余都是长列 我想将此数据转换为标签 特征数据框 我已经
  • Spark - 构建时出现 scala 初始化错误

    我正在尝试在我的 scala 应用程序中使用 Spark 这是我正在使用的 Spark 依赖项
  • 一起调用distinct和map会在spark库中抛出NPE

    我不确定这是否是一个错误 所以如果你这样做 d spark RDD String d distinct map x gt d filter equals x 您将获得 Java NPE 但是如果你做了一个collect之后立马distinc
  • 带有泛型参数的抽象类的 JsonFormat

    我正在尝试为具有通用参数的抽象类编写 JsonFormat 如下所示 abstract class Animal A def data A def otherStuff String stuff case class CatData cat
  • 条件运算符不能隐式转换?

    我对这个 C 的小怪癖有点困惑 给定变量 Boolean aBoolValue Byte aByteValue 编译如下 if aBoolValue aByteValue 1 else aByteValue 0 但这不会 aByteValu
  • 如何对 RDD 进行分区

    我有一个文本文件 其中包含大量由空格分隔的随机浮动值 我正在将此文件加载到 scala 中的 RDD 中 这个RDD是如何分区的 另外 是否有任何方法可以生成自定义分区 以便所有分区都具有相同数量的元素以及每个分区的索引 val dRDD
  • 将带有两层分隔符的字符串转换为字典 - python

    给定一个字符串 s x t1 ny t2 nz t3 我想转换成字典 sdic x 1 y 2 z 3 我通过这样做让它工作 sdic dict tuple j split t for j in i for i in s split n F
  • 如何检测数据库类型?

    我需要确保我连接的数据库是 MySQL 而不是 PostgreSQL 或 Microsoft SQL Server 我怎样才能知道正在使用哪种类型的数据库 第一个提示可能是如果您尝试使用 mySQL 数据库驱动程序连接到 PostgreSQ
  • 如何在 Scala mutable.Seq 上追加或前置

    Scala 有一些我不明白的地方collection mutable Seq http www scala lang org api current index html scala collection mutable Seq 它描述了所
  • Scalaz 状态 monad 示例

    我还没有看到很多 scalaz 状态单子的例子 有这个例子 http scalaz github com scalaz scalaz 2 9 1 6 0 2 doc sxr scalaz example ExampleState scala
  • 如何在平面级别将 JsValue 合并到 JsObject

    我有两个从案例类创建的 JsValue 即书籍和书籍详细信息 val bookJson Json tojson Book val bookDetailJson Json tojson BookDetail 格式为 Book id 1 nam
  • 使用 Elastic4s 进行动态 ElasticSearch 映射

    我有一个文档要在elasticSearch上建立索引 该文档包含一些我事先无法知道的动态键 例如以下示例中的 西班牙语 或 法语 contents title spanish Hola amigos french Bonjour les a
  • 使用 Either 处理 Scala 代码中的故障

    Optionmonad 是 Scala 中处理有或无事物的一种很好的表达方式 但是 如果在 什么也没发生 时需要记录一条消息怎么办 根据 Scala API 文档 Either 类型通常用作 scala Option where Left
  • Spark Collect_list 并限制结果列表

    我有以下格式的数据框 name merged key1 internalKey1 value1 key1 internalKey2 value2 key2 internalKey3 value3 我想做的是将数据框分组name 收集列表并l
  • Scala Monad - 完整的示例[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi

随机推荐

  • Qt - 在新线程中处理 QTcpSocket

    尝试在全局线程池中的新线程中处理已连接的客户端套接字 m threadPool QThreadPool globalInstance void TCPListenerThread onNewConnection QTcpSocket cli
  • EF Fluent Api 可以设置最小长度吗?

    看来使用Fluent API具有更大的灵活性 所以我选择遵循的方式始终使用 Fluent API 来确定它在 db 中具有的功能 而不是使用注释 但这是问题 我找不到设置最小长度的方法 有没有一种方法可以执行此操作 我注意到 没有太多的话题
  • 如果您知道在到达有效区域末尾之前会找到该字符,那么以太长的长度调用 memchr 是否合法?

    Is the following defined behavior in C11 and C 111 bool has4 char buf 10 0 1 2 4 return memchr buf 4 20 这里我们传递了一个太长的长度me
  • 使用 onResume 方法重新启动 Activity

    我想使用 onResume 方法重新启动活动 我以为我可以使用 Intent 来实现这一点 但这会导致无限循环 Override protected void onResume Intent intent new Intent MainAc
  • 如何更改图表标签的前景色?

    how can i change the fore color of the chart labels here s a screenshot of the chart 我尝试使用chart1 series 0 FontForeColor
  • 如何对已排序组内的组进行排序?

    dplyr 功能有一些额外的复杂性 我无法解决 主要是 我想对已经排序的组中的第二组进行排序 所以我有这个数据框 a table lt data frame id 1 30 grp1 sample LETTERS 1 5 30 replac
  • CAShapeLayer 的框架和边界

    我正在研究CAShapeLayer 并尝试绘制非线性路径 我想将框架设置为CAShapeLayer 所以我可以使用CGPathGetPathBoundingBox从中获取帧的方法CGPathRef 这是代码 CGMutablePathRef
  • 什么是<?超级 T> 语法? [复制]

    这个问题在这里已经有答案了 我无法理解以下语法 public class SortedList lt T extends Comparable lt super T gt gt extends LinkedList lt T gt 我看到类
  • Mockito junit 5 模拟构造函数

    我想模拟构造函数并在调用构造函数时返回模拟对象 这可以使用 powermockito 的 whenNew 方法来实现 如下所示 PowerMockito whenNew ClassName class withAnyArguments th
  • 从文本文件中删除特定行?

    我需要从文本文件中删除确切的一行 但我一生都无法弄清楚如何执行此操作 任何建议或例子将不胜感激 相关问题 从文本文件中删除行的有效方法 C 如果要删除的行是基于该行的内容 string line null string line to de
  • d3.js 中子节点朝向多个侧面的树(类似于家谱)

    var treeData name Device parent null var treeData2 name Device parent null document ready function var margin top 20 rig
  • SQLAlchemy @property 使用 dump_only 在 Marshmallow 中导致“未知字段”错误

    我正在使用flask marshmallow marshmallow v3 0 0rc1 flask marshmallow 0 9 0 和flask sqlalchemy sqlalchemy 1 2 16 flask sqlalchem
  • 矢量与数组性能

    在另一个线程中 我开始了关于向量和数组的讨论 其中我主要扮演魔鬼代言人的角色 按下按钮 然而 在这个过程中 我偶然发现了一个让我有点困惑的测试用例 我想对此进行一次真正的讨论 关于我因扮演魔鬼代言人而受到的 虐待 开始一个真正的测试用例 现
  • 在 Powershell 中使用参数调用 Invoke-Expression

    我用 C 编写了一个 powershell 模块 其中包含一堆 cmdlet 例如 Add VM 这些 cmdlet 会访问 API 并拉回数据 但为了与产品的 ssh CLI 保持一致 我编写了一个名为 newtask 的函数 它接受 a
  • Heroku Postgres:此连接已关闭

    我开发了一个小型 Heroku Grails Postreg 应用程序 部署后 20 分钟左右它就会正常工作 之后我总是得到 This connection has been closed Stacktrace follows Heroku
  • 从 Eclipse 查看本机代码

    我有一个使用本机方法调用的 java 应用程序 有没有办法在 eclipse 中查看此代码 我可以获得这个本机库的源代码 但不知道如何在 IDE 中链接 如果有人告诉我如何调试这个本机方法 也会很有帮助 不是完整的答案 但 Eclipse
  • 如何在 Javascript 中使用循环生成事件处理程序? [复制]

    这个问题在这里已经有答案了 例如 我有 10 个从 AJAX 响应生成的 a 标签 a href b1 a a href b2 a a href b3 a a href b4 a a href b5 a a href b6 a a href
  • 如何监听VueJS中的所有自定义事件?

    在我的 VueJS 应用程序中 我有一个 Vue 实例 我将其用作事件总线 用于在组件之间发送数据 就是这样 import Vue from vue export const EventBus new Vue 然后在我的组件中导入 Even
  • 将多个选择器与伪类关联

    有没有办法让多个选择器与伪类关联 换句话说 我想让它在悬停或聚焦于锚点 图像或按钮时 它们周围会有一个特殊的边框 我尝试过这个 在黑暗中拍摄 a button img hover a button img focus border 2px
  • 隐式使用 Scala 实现类型相等

    我一直在阅读一些有关 Scala 类型级编程的内容 主要是 Apocalisp 博客 还有 Alexander Lehmann 的 YouTube 演讲 我有点坚持我认为可能非常基本的事情 即使用隐式比较两种类型 如下所示 implicit