Scala 抽象路径依赖类型问题第 2 部分

发布于 2024-09-11 08:26:03 字数 992 浏览 6 评论 0原文

关于 Scala 抽象类型的几个问题。

  1. 如果我想在构造函数值中使用该类型,是否必须使用参数化 [] 类型? IE。是否可以有一个具有抽象构造函数参数类型的类?如果我摆脱 [T1,T2] 并使用 INode#TKey 和 INode#TValue 作为构造函数参数类型,我在做什么?我收到令人困惑的错误消息。
  2. 如何在不诉诸内部类型的情况下很好地解决这个问题?我在定义中使用 INode 似乎意味着我可以返回/接收具有不同类型的 TKey 和 INode。 T值。如何将其限制为与当前类型相同的 TKey/TValue 类型,而不限制自己返回/接收确切的“this”实例?
trait AbsTypes
{
  type TKey
  type TValue
}  
trait INode extends AbsTypes
{
  def get(key : TKey) : TValue
  def set(key : TKey, v : TValue) : INode
  def combine(other : INode, key : TKey): INode
}  
class ANode[T1,T2](
  val akey : T1,
  val aval : T2
) extends INode
{
  type TKey = T1
  type TValue = T2
  type Node = ANode[T1,T2]  
  def get(key : TKey) : TValue = { aval }
  def set(key : TKey, v : TValue) : INode = {
    new ANode(key,v)
  }
  def combine(other : INode, key :TKey) : INode = {
    //ERROR : type mismatch;  found   : key.type (with underlying type ANode.this.TKey)  required: other.TKey
    other.set(key, aval)
  }
}

Couple of questions on scala abstract types.

  1. Do I have to use parameterized [] types if I want to use the type in a constructor value? ie. is it possible to have a class with abstract constructor parameter types? If I get rid of [T1,T2] and use INode#TKey and INode#TValue as the constructor parameter types, what am I doing? I get confusing error messages.
  2. How do I solve this nicely without resorting to inner types? My use of INode in the definitions seem to be implying I could return / receive an INode with different types for TKey & TValue. How do I restrict it to the same TKey/TValue types as my current type, without restricting myself to returning/receiving exactly "this" instance?
trait AbsTypes
{
  type TKey
  type TValue
}  
trait INode extends AbsTypes
{
  def get(key : TKey) : TValue
  def set(key : TKey, v : TValue) : INode
  def combine(other : INode, key : TKey): INode
}  
class ANode[T1,T2](
  val akey : T1,
  val aval : T2
) extends INode
{
  type TKey = T1
  type TValue = T2
  type Node = ANode[T1,T2]  
  def get(key : TKey) : TValue = { aval }
  def set(key : TKey, v : TValue) : INode = {
    new ANode(key,v)
  }
  def combine(other : INode, key :TKey) : INode = {
    //ERROR : type mismatch;  found   : key.type (with underlying type ANode.this.TKey)  required: other.TKey
    other.set(key, aval)
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

骄傲 2024-09-18 08:26:03

我认为参数化类型非常适合您的设置。抽象类型绑定到特定实例,因此您会收到错误。我承认我没有尝试重构您的示例以使抽象类型起作用,但以下代码片段不会尝试混合抽象类型和参数化类型,并且 scalac 对此没有任何问题。

trait INode[TKey, TValue]
{
  type Node = INode[TKey, TValue]

  def get(key : TKey) : TValue
  def set(key : TKey, v : TValue) : Node
  def combine(other : Node, key : TKey): Node
}  
class ANode[TKey,TValue](
  val akey : TKey,
  val aval : TValue
) extends INode[TKey, TValue]
{
  def get(key : TKey) : TValue = aval
  def set(key : TKey, v : TValue) : Node = new ANode(key,v)
  def combine(other : Node, key : TKey) : Node = other.set(key, aval)
}

I think parameterized types fit nicely in your setting. Abstract types are bound to a particular instance, hence the error you are getting. I confess I didn't try to refactor your example to make abstract types work, but the following snippet doesn't try to mix abstract and parameterized types, and scalac has no problems with it.

trait INode[TKey, TValue]
{
  type Node = INode[TKey, TValue]

  def get(key : TKey) : TValue
  def set(key : TKey, v : TValue) : Node
  def combine(other : Node, key : TKey): Node
}  
class ANode[TKey,TValue](
  val akey : TKey,
  val aval : TValue
) extends INode[TKey, TValue]
{
  def get(key : TKey) : TValue = aval
  def set(key : TKey, v : TValue) : Node = new ANode(key,v)
  def combine(other : Node, key : TKey) : Node = other.set(key, aval)
}
云归处 2024-09-18 08:26:03

首先我想指出,combine方法根本不使用other参数,你也可以写:

def combine(key : TKey) : Node = set(key, aval)

..但我猜它是这就是我们所追求的原则。

如果您有一个非常小的允许的 TKey 和 TValue 组合集(比如 2),您可以使用抽象类型策略并从代码中删除参数化类型:

trait INode 
{
  type TKey
  type TValue
  def get(key : TKey) : TValue 
  def set(key : TKey, v : TValue) : INode 
  def combine(other : INode, key : TKey): INode
}  

case class StringNode(
  val m_key : String,
  val m_val : String
) extends INode
{
  type TKey = String
  type TValue = String
  override def get(key : TKey) : TValue = { m_val }
  override def set(key : TKey, v : TValue) : INode =  new StringNode(key,v) 
  override def combine(other : INode, key :TValue): INode  = {
    other match {
      case node: StringNode => node.set(key, m_val)
      case _ => throw new IllegalArgumentException("Not OK bla bla")
    }
  }
}

在我的示例中,您将在 < 的方法中得到一些代码重复code>StringNode 和 IntNode ,但至少说明了抽象类型的原理。

First of all I would like to point out that the combine method does not use the other parameter at all, you could just as well write:

def combine(key : TKey) : Node = set(key, aval)

.. but I guess it is just the principle we are after here.

If you have a very small set of allowed TKey and TValue combinations (say 2), you can use the abstract type tactics and cut parameterized types out of the code all together:

trait INode 
{
  type TKey
  type TValue
  def get(key : TKey) : TValue 
  def set(key : TKey, v : TValue) : INode 
  def combine(other : INode, key : TKey): INode
}  

case class StringNode(
  val m_key : String,
  val m_val : String
) extends INode
{
  type TKey = String
  type TValue = String
  override def get(key : TKey) : TValue = { m_val }
  override def set(key : TKey, v : TValue) : INode =  new StringNode(key,v) 
  override def combine(other : INode, key :TValue): INode  = {
    other match {
      case node: StringNode => node.set(key, m_val)
      case _ => throw new IllegalArgumentException("Not OK bla bla")
    }
  }
}

In my example you will get some code duplication in the methods of StringNode and, say, IntNode , but at least the principle of abstract types is illustrated.

冷默言语 2024-09-18 08:26:03

由于 other 是不同的 INode,因此它有自己的 TKeyTValue 类型。无法保证此ANodeTKeyTValueother 的相匹配。您需要使用相等或下限(我使用的)来约束类型。我没有尝试运行它,但以下代码针对 Scala 2.8.0 进行编译

  trait AbsTypes {
    type TKey
    type TValue
  }
  trait INode extends AbsTypes {
    def get(key : TKey) : TValue
    def set(key : TKey, v : TValue) : INode
    //def combine(other : INode, key : TKey): INode
    type TNode = INode { 
                   type TKey >: INode.this.TKey 
                   type TValue >: INode.this.TValue 
                 }
    def combine(other : TNode, key : TKey) : INode
  }
  class ANode[T1,T2](val akey : T1, val aval : T2) extends INode {
    type TKey = T1
    type TValue = T2
    type Node = ANode[T1,T2]
    def get(key : TKey) : TValue = { aval }
    def set(key : TKey, v : TValue) : INode = {
      new ANode(key,v)
    }
    def combine(other : TNode, key : TKey) : INode = {
      other.set(key, aval)
    }
  }

Since other is a different INode, it has it's own TKey and TValue types. There is no guarantee that TKey and TValue of this ANode match those of other. You need to constrain the types with either equality or lower bound (which I used). I didn't try to run it, but the following compiles against Scala 2.8.0

  trait AbsTypes {
    type TKey
    type TValue
  }
  trait INode extends AbsTypes {
    def get(key : TKey) : TValue
    def set(key : TKey, v : TValue) : INode
    //def combine(other : INode, key : TKey): INode
    type TNode = INode { 
                   type TKey >: INode.this.TKey 
                   type TValue >: INode.this.TValue 
                 }
    def combine(other : TNode, key : TKey) : INode
  }
  class ANode[T1,T2](val akey : T1, val aval : T2) extends INode {
    type TKey = T1
    type TValue = T2
    type Node = ANode[T1,T2]
    def get(key : TKey) : TValue = { aval }
    def set(key : TKey, v : TValue) : INode = {
      new ANode(key,v)
    }
    def combine(other : TNode, key : TKey) : INode = {
      other.set(key, aval)
    }
  }
雨落星ぅ辰 2024-09-18 08:26:03

感谢您的回答,他们是最有帮助的&确实帮助了我的理解。
我发现的一种替代解决方案如下。 AbsType 不需要是基类,您可以使用它来包装参数化类型定义。


class ANode[T <: AbsTypes](
  val akey : T#TKey,
  val aval : T#TValue
) extends INode[T]
{
    def get(key : TKey) : TValue = { aval }
    def set(key : TKey, v : TValue) : Node = {
        new ANode[T](key,v)
    }
    def combine(other : Node, key : TKey) : Node = {
        other.set(key, aval)  // "use" this & other somehow
    }
}

// Examples
class AbsTypeDef[TKey1, TValue1] extends AbsTypes
{
    type TKey = TKey1
    type TValue = TValue1
}

object ANode
{
    type AIntString = AbsTypeDef[Int,String]
    type AStringLong = AbsTypes { type TKey = String; type TValue = Long}
    def n1 = new ANode[AIntString](1,"one")
    def n1b = new ANode[AIntString](2,"two")
    def n2 = new ANode[AStringLong]("two",2L)
    n1.combine(n1b,2)
    //n1.combine(n2,2)  // compile error
}

Thanks for your answers, they're most helpful & have really helped my understanding.
One alternative solution I've found is below. The AbsType doesn't need to be the base class, you can use it to wrap up the parameterized type definitions.


class ANode[T <: AbsTypes](
  val akey : T#TKey,
  val aval : T#TValue
) extends INode[T]
{
    def get(key : TKey) : TValue = { aval }
    def set(key : TKey, v : TValue) : Node = {
        new ANode[T](key,v)
    }
    def combine(other : Node, key : TKey) : Node = {
        other.set(key, aval)  // "use" this & other somehow
    }
}

// Examples
class AbsTypeDef[TKey1, TValue1] extends AbsTypes
{
    type TKey = TKey1
    type TValue = TValue1
}

object ANode
{
    type AIntString = AbsTypeDef[Int,String]
    type AStringLong = AbsTypes { type TKey = String; type TValue = Long}
    def n1 = new ANode[AIntString](1,"one")
    def n1b = new ANode[AIntString](2,"two")
    def n2 = new ANode[AStringLong]("two",2L)
    n1.combine(n1b,2)
    //n1.combine(n2,2)  // compile error
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文