通过类型成员而不是类型参数进行 F 有界量化? [英] F-bounded quantification through type member instead of type parameter?

查看:41
本文介绍了通过类型成员而不是类型参数进行 F 有界量化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将类型参数移动到类型成员.

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#TxS#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.Txour 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆