swift - 为什么 type(of:) 返回 Metatype,而不是 T.Type?
问题描述
我注意到type(of:)
有这个意想不到的签名:
func type<T, Metatype>(of value: T) -> Metatype
而我本来期望:
func type<T>(of value: T) -> T.Type
实际签名以某种方式采用 2 个独立且无界的类型参数,将其中一个用作参数类型,另一个用作返回类型。这似乎表明我可以做一些像这样愚蠢的事情:
let foo: String = type(of: 1) // T is Int, Metatype is String
但是当我真正尝试时,
无法将“Int.Type”类型的值转换为指定类型“String”
所以我毕竟没有指定Metatype
,即使它是一个泛型类型参数。我很好奇编译器是怎么做到的,所以我去源代码查了一下。我认为阻止我做那件愚蠢事情的是这个@_semantics
注释:
@_transparent
@_semantics("typechecker.type(of:)")
public func type<T, Metatype>(of value: T) -> Metatype {
我还看到一些评论解释说这里写的实现实际上并没有被调用,这是由类型检查器本身直接处理的。但这并不能回答我的问题——为什么type(of:)
返回一个完全不相关的类型参数Metatype
,而不是T.Type
?
我的猜测是,在某些极端情况下,type(of:)
会返回一个完全不相关的类型T
,但我不知道那是什么。
解决方案
tl;dr:的行为type(of:)
取决于T
是存在的还是具体的,类型系统不能在语法上有效地反映实际的返回类型,所以直接在类型检查系统中处理。Metatype
在代码中明确地不被约束为相同,T
以便可以专门化有效行为。Metatype
并且T
不一定相关。
type(of:)
特殊之处在于它的行为因传递给它的类型而异。具体来说,它对存在类型有特殊的行为,因为它能够通过存在框来获取传入值的底层类型。例如:
func myType<T>(of value: T) -> T.Type {
return T.self
}
protocol Foo {}
struct X: Foo {}
let x = X()
print(type(of: x), "vs.", myType(of: x)) // => X vs. X
let f: Foo = X()
print(type(of: f), "vs.", myType(of: f)) // => X vs. Foo
当给定一个存在类型时,Foo
返回类型T.Type
只能返回存在本身的元类型(即),而不是存在容器内的值的元类型( )。因此,不是返回,而是返回一个不相关的类型,该类型绑定到类型检查器本身中的正确类型。这是您正在寻找的边缘案例:Foo.self
X.self
T.Type
type(of:)
Metadata
我的猜测是,在某些极端情况下,
type(of:)
会返回一个完全不相关的类型T
,但我不知道那是什么。
如果您查看lib/Sema/TypeChecker.h
,您可以看到几个 stdlib 函数类型的一些特殊语义声明:
/// Special-case type checking semantics for certain declarations.
enum class DeclTypeCheckingSemantics {
/// A normal declaration.
Normal,
/// The type(of:) declaration, which performs a "dynamic type" operation,
/// with different behavior for existential and non-existential arguments.
TypeOf,
/// The withoutActuallyEscaping(_:do:) declaration, which makes a nonescaping
/// closure temporarily escapable.
WithoutActuallyEscaping,
/// The _openExistential(_:do:) declaration, which extracts the value inside
/// an existential and passes it as a value of its own dynamic type.
OpenExistential,
};
这里的关键之一是TypeOf
,它确实是为具有@_semantics("typechecker.type(of:)")
您记下的属性的函数返回的。(您可以看到该属性是如何签入的TypeChecker::getDeclTypeCheckingSemantics
)
如果你去寻找 的用法TypeOf
,类型检查有两个关键位置:
getTypeOfReferenceWithSpecialTypeCheckingSemantics
它在类型检查器约束系统中注入类型约束。type(of:)
在这里作为重载处理,因为Metadata
实际上并没有绑定;这里的约束求解器应用了一个有效的类型检查约束,该约束约束Metadata
为value
. 这里的关键是以type(of:)
这种方式编写的,因此它会是一个重载,并在此处处理。ExprRewriter::finishApply
它在 AST 中执行实际表达式重写,以将返回类型替换为值的有效实际类型
从(1):
// Proceed with a "DynamicType" operation. This produces an existential
// metatype from existentials, or a concrete metatype from non-
// existentials (as seen from the current abstraction level), which can't
// be expressed in the type system currently.
回顾一些历史——这是在提交1889fde2284916e2c368c9c7cc87906adae9155b中实现的。乔的提交信息很有启发性:
通过重载解析
type(of:)
而不是解析hackery来解决。
type(of:)
具有其类型在 Swift 的类型系统中不能直接表示的行为,因为它产生了具体的和存在的元类型。在 Swift 3 中,我们添加了一个解析器 hack 来type(of: <expr>)
转换为 DynamicTypeExpr,但这实际上是type(of:)
一个保留名称。将其置于与其他声明相同的级别更有原则,即使它具有特殊情况的类型系统行为,如果出现在重载集中Swift.type(of:)
,我们可以通过对重载决策期间生成的类型系统进行特殊处理来做到这一点。Swift.type(of:)
这也为处理我们希望表面上表现得像普通声明但具有其他不可表达类型的其他声明奠定了基础,即。withoutActuallyEscaping
从 SE-0110。
从那时起,正如我们从WithoutActuallyEscaping
和中看到的OpenExistential
,其他特殊函数已被重写以利用这一点。
推荐阅读
- java - ubuntu for java中的环境变量
- r - 如何在 plot_ly 的 scatter3d 中绘制所有巨大的数据点?
- javascript - 禁止在第三方渠道使用 emojireact-role
- postgresql - 从服务器资源管理器连接到 PostgreSQL 时出错 - 无法加载文件或程序集,版本 = 4.0.4.1 'System.Runtime.CompilerServices.Unsafe'
- java - 如何使用没有密码的 SSH 身份验证连接到 Windows SFTP 服务器?
- google-apps-script - 有没有办法使用脚本生成谷歌表格图表的 .jpg 图像?
- elasticsearch - 为什么 elasticsearch 不支持 min_doc_count 和 order by _count asc?
- javascript - 如何更改更改选择标签上的值
- r - 从 Cronr R 包创建的 Cron 作业停止执行而没有错误消息进行调试
- php - 如何在 PhpWord 中用 templateProcessor 替换页眉/页脚值?