通过型部件,而不是类型参数F-界定量化?(F-bounded quantification thro

2019-07-18 00:21发布

我想一个类型参数移动到一个类型的成员。

这是一个起点,其工作原理:

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#TxS#Id变成了莫名其妙分离:

error: could not find implicit value for parameter tx: _9.Tx
               id.dispose()
                         ^

任何技巧或变更,使工作?


编辑 :为了澄清,我主要希望解决的类型SSys ,使其工作。 有使用路径依赖类型的我来说,许多问题。 举一个例子,反映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 <: IdentifierS <: 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类型和其他一切( SysIdentifier为内部特质),并保留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#IdS#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?