通过类型成员而不是类型参数进行 F 有界量化? [英] F-bounded quantification through type member instead of type parameter?
问题描述
我想将类型参数移动到类型成员.
I would like to move a type parameter to a type member.
这是有效的起点:
trait Sys[S <: Sys[S]] {
type Tx
type Id <: Identifier[S#Tx]
}
trait Identifier[Tx] {
def dispose()(implicit tx: Tx): Unit
}
trait Test[S <: Sys[S]] {
def id: S#Id
def dispose()(implicit tx: S#Tx) {
id.dispose()
}
}
令我烦恼的是,我在整个库中都携带了一个类型参数 [S <: Sys[S]]
.所以我的想法是这样的:
What annoys me is that I'm carrying around a type parameter [S <: Sys[S]]
throughout my entire libraries. So what I was thinking is this:
trait Sys {
type S = this.type // ?
type Tx
type Id <: Identifier[S#Tx]
}
trait Identifier[Tx] {
def dispose()(implicit tx: Tx): Unit
}
trait Test[S <: Sys] {
def id: S#Id
def dispose()(implicit tx: S#Tx) {
id.dispose()
}
}
哪个失败了... S#Tx
和 S#Id
以某种方式分离:
Which fails... S#Tx
and S#Id
became somehow detached:
error: could not find implicit value for parameter tx: _9.Tx
id.dispose()
^
有什么技巧或改变使它起作用吗?
Any tricks or changes that make it work?
EDIT :澄清一下,我主要希望修复 Sys
中的 S
类型以使其工作.在我的情况下,使用依赖于路径的类型有很多问题.举一个反映pedrofuria和Owen回答的例子:
EDIT : To clarify, I am primarily hoping to fix the type S
in Sys
to make it work. There are numerous problems in my case using path-dependent types. To give just one example which reflects the answers of pedrofuria and Owen:
trait Foo[S <: Sys] {
val s: S
def id: s.Id
def dispose()(implicit tx: s.Tx) {
id.dispose()
}
}
trait Bar[S <: Sys] {
val s: S
def id: s.Id
def foo: Foo[S]
def dispose()(implicit tx: s.Tx) {
foo.dispose()
id.dispose()
}
}
<console>:27: error: could not find implicit value for parameter tx: _106.s.Tx
foo.dispose()
^
试着让 def foo: Foo[s.type]
让你知道这无济于事.
Try to make that def foo: Foo[s.type]
to give you an idea that this leads nowhere.
推荐答案
这与其说是一个答案,不如说是对 pedrofurla 的回答的评论;我认为这是正确的.让我解释一下原因.
This is not so much an answer as a comment on pedrofurla's answer; which I think is correct. Let me explain why.
Scala 有一个有趣的地方,当你编写一个类的类型成员时,它本质上创建了两个不同的名字,一个属于这个类,另一个属于那个类的对象.它们之间存在某种联系,即对象成员类型必须是类成员类型的子类型,但根据我的经验,您很少想使用这种联系;大多数情况下,您应该将它们视为完全独立的事物.
Scala has this funny thing where, when you write a type member of a class, it essentially creates two different names, one of which belongs to the class, and the other of which belongs to objects of that class. There is some connection between them, namely that the object member type has to be a subtype of the class member type, but in my experience you very rarely want to use this connection; most of the time you should think of them as entirely separate things.
您在这里真正想做的是打包两种类型,以便您可以为它们命名.所以我会像这样写 Sys
:
What you really wanted to do here is package up two types so that you can give a name to the pair of them. So I would write Sys
like:
trait Sys {
type Tx
type Id <: Identifier[Tx]
}
因为它准确地说明了您想要做什么,没有任何魔法或浮夸:创建一个对象类型,每个对象存储两个东西,这些东西是类型(并且它们之间有一些约束).
because that says exactly what you want to do, with no magic or fluff: create a type of objects, each of which stores two things, and those things are types (and have some constraints between them).
然后你可以按照pedrofurla建议的方式编写Test
:
Then you can write Test
the way pedrofurla suggestes:
trait Test {
val s: Sys
def id: s.Id
def dispose()(implicit tx: s.Tx) {
id.dispose()(tx)
}
}
同样,只需要你需要的东西,没有额外的东西:要创建一个 Test
的实例,你必须提供一个 Sys
,以及那个 实例Sys
将包含 Test
需要使用的类型.
Again, only what you need and nothing extra: to create an instance of Test
, you must supply a Sys
, and that instance of Sys
will contain the types that Test
needs to work with.
换句话说,有时只是将类型视为要打包和传递的常规旧值.
In other words, sometimes just think of types as regular old values to be packaged up and passed around.
编辑:
可扩展性(至少在你的例子中,可能还有其他我没有想到的)如果你再次坚持你所需要的,应该不会成为问题.在您的 Foo
/Bar
示例中,
Scalability (at least in your example, there may be others I haven't thought of) should not be a problem if you again stick to exactly what you need. In your Foo
/Bar
example,
// This is normal; nothing unexpected.
trait Foo {
val s: Sys
def id: s.Id
def dispose()(implicit tx: s.Tx) {
id.dispose()
}
}
trait Bar { self =>
val s: Sys
def id: s.Id
// Now here's the key!
val foo: Foo { val s: Sys { type Tx = self.s.Tx } }
def dispose()(implicit tx: s.Tx) {
foo.dispose()
id.dispose()
}
}
这里,我们真正希望我们的 foo
是它的 s.Tx
和 our s.Tx 是一样的
,因为我们要做的是交替使用它们.所以,我们只需要它,它就可以毫无问题地编译.
Here, what we really desire of our foo
is that it's s.Tx
is the same as our s.Tx
, because what we want to do is use them interchangeably. So, we just require exactly that, and it compiles with no problems.
这篇关于通过类型成员而不是类型参数进行 F 有界量化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!