scala - 函数定义中返回类型后的大括号
问题描述
在阅读猫库的 Functor 源代码时,我无法理解函数toFunctorOps的返回类型之后的花括号是做什么的;我的猜测是这个块将作为构造函数的一部分执行?如果是这样,那么为什么类型TypeClassType使用相同的代码类型 TypeClassType = Functor[F]定义了两次?
trait Ops[F[_], A] extends Serializable {
type TypeClassType <: Functor[F]
def self: F[A]
val typeClassInstance: TypeClassType
def map[B](f: A => B): F[B] = typeClassInstance.map[A, B](self)(f)
...
}
trait ToFunctorOps extends Serializable {
implicit def toFunctorOps[F[_], A](target: F[A])(implicit tc: Functor[F]): Ops[F, A] {
type TypeClassType = Functor[F]
} =
new Ops[F, A] {
type TypeClassType = Functor[F]
val self: F[A] = target
val typeClassInstance: TypeClassType = tc
}
}
解决方案
我不明白返回类型之后的花括号是什么......
细化对 trait{ type TypeClassType = Functor[F] }
的类型成员施加了进一步的约束。换句话说,它为编译器提供了有关方法的特定返回类型的更多信息TypeClassType
Ops
toFunctorOps
Ops[F, A] { type TypeClassType = Functor[F] }
请注意,细化块被认为是返回类型的一部分,与构造函数无关。
让我们简化类型以更好地说明概念,因此考虑
trait Foo {
type A
def bar: A
}
val foo: Foo = new Foo {
type A = Int
def bar: A = ???
}
val x: foo.A = 42 // type mismatch error
请注意,变量的静态类型如何foo
不包括类型成员A
已实例化到的特定信息Int
。现在让我们通过使用类型细化向编译器提供这些信息
val foo: Foo { type A = Int } = new Foo {
type A = Int
def bar: A = ???
}
val x: foo.A = 42 // ok
现在编译器知道类型成员A
恰好是一个Int
.
类型类的设计者在何时使用类型成员而不是类型参数方面做出明智的决定,有时甚至会像您的情况一样将两者混合使用。例如 traitFoo
可以像这样被参数化
trait Foo[A] {
def bar: A
}
val foo: Foo[Int] = new Foo[Int] {
def bar: Int = ???
}
并且编译器将再次获得类型参数A
已实例化为的精确信息Int
。
为什么类型 TypeClassType 被定义了两次
精炼类型Foo { type A = Int }
是 的较窄子类型Foo
,类似于Cat
的较窄子类型Animal
implicitly[(Foo { type A = Int }) <:< Foo]
implicitly[Cat <:< Animal]
所以即使右侧表达式实例A
化为Int
,左侧显式告诉编译器静态类型foo
只是更宽的超类型Foo
val foo: Foo = new Foo {
type A = Int
def bar: A = ???
}
类似于编译器如何知道zar
下面的静态类型只是更广泛的超类型Animal
,尽管 RHS 上的表达式指定了Cat
val zar: Animal = new Cat
因此需要“双重”类型规范
val foo: Foo { type A = Int } = new Foo {
type A = Int
...
}
如同
val zar: Cat = new Cat
我们可以尝试依靠推理来推断最具体的类型,但是当我们显式注释类型时,我们必须通过细化提供包括类型成员约束在内的完整信息。
推荐阅读
- c++ - 为什么 keybd_event 不能与“Shift Key”一起正常工作?
- next.js - 无法在 Vercel 上使用 serverSideTranslations
- python - 如何从数据框中提取特定值的索引和列?
- javascript - 如何避免 Jshint(在引用外部范围变量的循环中声明的函数可能会导致语义混淆)
- javascript - 将 Handlebars 与 Node.js/Express 一起使用,需要登录和注销才能根据用户登录进行渲染
- kotlin - Jetbrains Compose 的 Kotlin 内部错误 java.lang.IllegalStateException
- java - 通过 Java 文件使用 Avro Schema 别名
- javascript - Gsap 和 ReactJs。它可以工作/编译,但我有一个控制台错误。“未捕获的 TypeError:callback.call 不是函数”
- xamarin.forms - Xamarin Forms 数字键盘 - 启用空格和破折号按钮
- php - SHA512 转字符串,PHP