如何解决函数定义中返回类型后的大括号
阅读cats library's Functor source时,我无法理解toFunctorOps函数的返回类型后的卷曲块是做什么的;我的猜测是这个块将作为构造函数的一部分执行?如果是这样,那么为什么类型 TypeClasstype 用相同的代码定义了两次 type 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
}
}
解决方法
我不明白返回类型后面的卷曲块是什么......
细化 { 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
。
类型类的设计者在何时使用类型成员而不是类型参数方面做出明智的决定,有时甚至在您的情况下两者混合使用。例如 trait Foo
可以像这样被参数化
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
的静态类型只是更广泛的超类型 { {1}}
Foo
类似于编译器如何知道下面的 val foo: Foo = new Foo {
type A = Int
def bar: A = ???
}
静态类型只是更广泛的超类型 zar
,尽管 RHS 上的表达式指定了 Animal
Cat
因此需要“double”类型规范
val zar: Animal = new Cat
类似
val foo: Foo { type A = Int } = new Foo {
type A = Int
...
}
我们可以尝试依靠推理来推断出最具体的类型,但是当我们显式注释类型时,我们必须通过细化提供包括类型成员约束的完整信息。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。