1

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

  1. 如果我想在构造函数值中使用类型,是否必须使用参数化 [] 类型?IE。是否可以有一个具有抽象构造函数参数类型的类?如果我摆脱 [T1,T2] 并使用 INode#TKey 和 INode#TValue 作为构造函数参数类型,我在做什么?我收到令人困惑的错误消息。
  2. 我如何在不求助于内部类型的情况下很好地解决这个问题?我在定义中使用 INode 似乎意味着我可以返回/接收具有不同类型的 TKey 和 TValue 的 INode。如何将其限制为与当前类型相同的 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)
  }
}
4

4 回答 4

2

首先我想指出,该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")
    }
  }
}

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

于 2010-07-26T11:23:02.093 回答
2

由于other是不同的INode,它有它自己的TKeyTValue类型。无法保证TKey和 与TValueANode匹配other。您需要使用相等或下限(我使用过)来约束类型。我没有尝试运行它,但以下针对 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)
    }
  }
于 2010-07-26T13:02:18.667 回答
2

我认为参数化类型非常适合您的设置。抽象类型绑定到特定实例,因此您得到的错误。我承认我没有尝试重构您的示例以使抽象类型起作用,但是以下代码段没有尝试混合抽象类型和参数化类型,并且 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)
}
于 2010-07-26T09:49:47.043 回答
0

感谢您的回答,它们对我很有帮助,并且确实帮助了我的理解。我发现的另一种解决方案如下。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
}
于 2010-07-28T03:07:49.700 回答