Скала абстрактный путь зависимый тип проблемы часть 2 - PullRequest
1 голос
/ 26 июля 2010

Пара вопросов по абстрактным типам scala.

  1. Нужно ли использовать параметризованные типы [], если я хочу использовать тип в значении конструктора? то есть. возможно ли иметь класс с абстрактными типами параметров конструктора? Если я избавлюсь от [T1, T2] и использую INode # TKey и INode # TValue в качестве типов параметров конструктора, что я буду делать? Я получаю запутанные сообщения об ошибках.
  2. Как мне решить эту проблему, не прибегая к внутренним типам? Мое использование INode в определениях, кажется, подразумевает, что я могу вернуть / получить INode с разными типами для TKey & TValue. Как я могу ограничить его теми же типами TKey / TValue, что и мой текущий тип, не ограничиваясь возвратом / получением именно этого «экземпляра»?
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 ]

2 голосов
/ 26 июля 2010

Прежде всего, я хотел бы отметить, что метод 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 и, скажем, IntNode, но по крайней мере проиллюстрирован принцип абстрактных типов.

2 голосов
/ 26 июля 2010

Поскольку other отличается от INode, у него есть свои типы TKey и TValue. Нет гарантии, что TKey и TValue этого ANode совпадают с 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)
    }
  }
2 голосов
/ 26 июля 2010

Я думаю, что параметризованные типы хорошо вписываются в ваши настройки.Абстрактные типы привязаны к конкретному экземпляру, поэтому вы получаете ошибку.Признаюсь, я не пытался реорганизовать ваш пример, чтобы заставить работать абстрактные типы, но следующий фрагмент не пытается смешивать абстрактные и параметризованные типы, и у 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)
}
0 голосов
/ 28 июля 2010

Спасибо за ваши ответы, они наиболее полезны и действительно помогли мне понять.Одно альтернативное решение я нашел ниже.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
}
...