我想一个类型参数移动到一个类型的成员。
这是一个起点,其工作原理:
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]]
贯穿我的整个库。 所以,我在想是这样的:
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
变成了莫名其妙分离:
error: could not find implicit value for parameter tx: _9.Tx
id.dispose()
^
任何技巧或变更,使工作?
编辑 :为了澄清,我主要希望解决的类型S
在Sys
,使其工作。 有使用路径依赖类型的我来说,许多问题。 举一个例子,反映pedrofuria和欧文的答案:
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]
给你一个想法,这走不通。
Answer 1:
这里是一个版本的Test
,编译:
trait Test[S <: Sys] {
val s : S
def id: s.Id
def dispose()(implicit tx: s.Tx) {
id.dispose()
}
}
你绝对正确地说“S#的Tx和S#标识变得莫名其妙分离”。 你不能保证,在能够对S的其实都是同一类型的,据我所知。
Answer 2:
这与其说是一个答案,在pedrofurla的回答评论; 我认为这是正确的。 让我来解释为什么。
Scala有在那里,当你写一个类的类型成员,它本质上创建了两个不同的名字,其中一个属于这一类有趣的事情,而另一个属于该类的对象。 它们之间有一定的联系,即对象成员类型必须是类成员类型的子类型,但在我的经验,你很少想使用此连接; 大部分的时间你应该把它们看成完全独立的东西。
你真正想要做的,是打包两种类型,这样就可以给一个名称,他们两人。 所以,我会写Sys
,如:
trait Sys {
type Tx
type Id <: Identifier[Tx]
}
因为,说正是你想做的事,没有魔法或绒毛:创建对象的类型,每个存储两件事情,而这些事情是类型(和它们之间有一定的限制)。
然后,你可以写Test
的方式pedrofurla suggestes:
trait Test {
val s: Sys
def id: s.Id
def dispose()(implicit tx: s.Tx) {
id.dispose()(tx)
}
}
同样,只有你需要什么,没有什么多余的:创建实例Test
,你必须提供一个Sys
,以及该实例 Sys
将包含该类型的Test
需要一起工作。
换句话说,有时只是觉得作为普通的旧值进行打包,并通过周围的类型。
编辑 :
可扩展性(至少在你的榜样,有可能是其他人我都没有想到的)不应该是一个问题,如果你再坚持你所需要的东西。 在您的Foo
/ Bar
例子,
// 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
是一样的我们 s.Tx
,因为我们想要做的是交替使用它们。 所以,我们只需要精确地说,它没有任何问题编译。
Answer 3:
虽然这并不能回答你的问题(确保现有的代码稍加修改),这里有一个想法:
代替Tx
类型为成员Sys
,并在使用Identifier
,我想,作为一个起点,使它的参数Sys
,并确保它正在以同样的方式由双方使用的Id <: Identifier
和S <: Sys
,就像这样:
trait Sys[Tx] {
type S <: Sys[Tx]
type Id <: Identifier[Tx]
}
trait Identifier[Tx] {
def dispose()(implicit tx: Tx): Unit
}
trait Test[Tx, S <: Sys[Tx]] {
def id: S#Id
def dispose()(implicit tx: Tx) = id.dispose()
}
这很难说是在对于你的动机的改进( Sys
仍然有一个类型参数),但我的下一个步骤将是转换Tx
键入成员。 我可以做但它的工作,而没有使用任何的唯一方法val s: S
挂羊头卖狗肉(和类型基于它)是:
- 拆分
Sys
分为两个特点,引入OuterSys
作为持有人Tx
类型和其他一切( Sys
和Identifier
为内部特质),并保留Sys
的任何其他它为你做的 - 有
Test
特质属于OuterSys
下面的代码:
trait OuterSys {
type Tx
type S <: Sys
type Id <: Identifier
trait Sys {
}
trait Identifier {
def dispose()(implicit tx: Tx): Unit
}
trait Test {
def id: Id
def dispose()(implicit tx: Tx) = id.dispose()
}
}
因此,尽管没有真正回答你的问题,或解决你的问题,我希望它可能至少可以给你们一些想法如何通过拉动这一点。 别的我什么都试过了回来,在我的编译器喊的一些实例S
和期待基于它的类型。
编辑 :对于分裂没有真正的需要Sys
:
trait Sys {
type Tx
type Id <: Identifier
trait Identifier {
def dispose()(implicit tx: Tx): Unit
}
trait Test {
def id: Id
def dispose()(implicit tx: Tx) = id.dispose()
}
}
也忽略提及明显的-这类型取决于Sys
的实例,我想是有道理的(系统之间没有共享标识符的交易也许?)。
无需“测试”从内Sys
的实例将会和不需要type S <: Sys
任何多个( type S = this.type
在MySystem):
object MySystem extends Sys {
type Tx = MyTransaction
type Id = MyIdentifier
class MyTransaction (...)
class MyIdentifier (...) extends Identifier {
def dispose()(implicit tx: MySystem.Tx) {}
}
}
object MyOuterTest {
{
def id: MySystem.Id = new MySystem.MyIdentifier(...)
def dispose()(implicit tx: MySystem.Tx) {
id.dispose()
}
}
Answer 4:
我有2个版本的编译,但我不能完全肯定要么是你在找什么在您的图书馆。 ( 编辑 :此版本是天生的缺陷,见注释)。 在这里,我们删除类型参数从Sys系统完全为S,并继续使用类型的预测(与路径依赖的类型)。
trait Sys {
type Tx
type Id <: Identifier[Sys#Tx]
}
trait Identifier[Tx] {
def dispose()(implicit tx: Tx)
}
trait Test[S <: Sys] {
def id: S#Id
def dispose()(implicit tx: S#Tx) {
id.dispose()(tx)
}
}
在这个版本中,我们的类型参数转换为一个类型的成员(我不能完全肯定这是正确的翻译),然后用型的细化和预测类型的组合,以确保在测试正确的类型。
trait Sys {
type S <: Sys
type Tx
type Id <: Identifier[S#Tx]
}
trait Identifier[Tx] {
def dispose()(implicit tx: Tx)
}
trait Test[A <: Sys {type S = A}] {
def id: A#Id
def dispose()(implicit tx: A#S#Tx) {
id.dispose()
}
}
还要注意,我们必须使用A#S#Tx
作为我们的投影类型的隐含参数,这有望带来了曙光了为什么S#Id
和S#Tx
成为“超脱”。 在现实中,它们不是分离的,声明type S = this.type
使得S
一个单型,然后使S#T
的路径依赖型。
为了更清楚,给出val a: A {type B}
aA
为速记a.type#A
。 即S#T
真是this.type#T
,这也是为什么简单地声明def dispose()(implicit tx: S#S#T)
将无法正常工作,因为S#S#T
是一种投射,而不是一个路径如所期望,如在该所要求的答案上面例举依赖型val s: S
来编译。
编辑 :您可以测试删除参数如下:
trait Test {
type A <: Sys {type S = A}
def id: A#Id
def dispose()(implicit tx: A#S#Tx) {
id.dispose()
}
}
然而,这可能需要大量的源代码被修改。
无论如果你使用类型参数或类型成员,指定类型不会就此消失无返工类型库中是如何工作的。 即,类型参数和抽象类型成员是等价的,所以它似乎并不认为你可以摆脱类型的S <: Sys[S]
完全。
EDIT2:不使用路径依赖的类型或沿杜读管的回答线的东西,这似乎并不可能。 这是一个轻微的修改,我已经给了避免绕过val s: S
,但是因为它需要改变它可能不使用,能在您的图书馆Identifier[Tx]
一类成员和def id: S#Id
来一个val
以暴露该路径依赖型:
trait Sys {self =>
type Tx
type Id <: Identifier {type Tx = self.Tx}
}
trait Identifier {
type Tx
def dispose()(implicit tx: Tx)
}
trait Test[S <: Sys] {
val id: S#Id
def dispose()(implicit tx: id.Tx) {
id.dispose()(tx)
}
}
文章来源: F-bounded quantification through type member instead of type parameter?