Обойти невозможность создать подкласс зависимого от пути типа

Я ищу способ ограничить вызовы определенных объектов. Для данной транзакционной системы, которая определяет ссылочный тип, тип транзакции и идентификатор:

trait Sys[S <: Sys[S]] {
  type Ref[A]
  type Tx <: Txn[S]
  type ID
}

Я хотел бы иметь возможность смешивать в черту, которая может быть использована для создания изменяемых ссылок:

trait Mutable[S <: Sys[S]] {
  def id: S#ID
  protected final def newRef[A](implicit tx: S#Tx): S#Ref[A] =
    tx.newRef[A](this)
}

И эталонная фабрика определяется как часть транзакции:

trait Txn[S <: Sys[S]] {
  /* private[Mutable] */ def newRef[A](mut: Mutable[S]): S#Ref[A]
  def newID: S#ID
}

Теперь проблема в том, что в следующей структуре я могу создавать ссылки с ложным контекстом:

def newStruct[S <: Sys[S]](cross: Mutable[S])(implicit tx: S#Tx) = 
  new Mutable[S] {
    val id        = tx.newID
    val allowed   = newRef[Int]
    val forbidden = tx.newRef[Int](cross) // shouldn't compile...
  }

Я хотел бы запретить последний звонок. Очевидно, я не могу сделать newRef в Txn личное для Mutable, так как Mutable не включающий класс Txn, Я также хотел бы не использовать конфиденциальность пакетов, так как можно легко взломать newRef путем определения объекта в этом пакете.

В идеале я хотел бы это:

trait Sys[S <: Sys[S]] { trait Mutable }

class MyStruct[S <: Sys[S]] extends S#Mutable { ... }

что бы решить все проблемы. Но это запрещено, так как S в S#Mutable"is not a legal prefix for a constructor" во вселенной скалака....

Спасибо за предложения!

1 ответ

Решение

Вы можете поставить определение Txn в Mutableобъект-компаньон, затем сделайте его закрытым для Mutable, Не уверен, есть ли дальнейшие последствия, все же.

trait Sys[ S <: Sys[ S ]] {
   type Ref[ A ]
   type Tx <: Txn[ S ]
   type ID
}

object Mutable {
  trait Txn[ S <: Sys[ S ]] {
    private[Mutable] def newRef[ A ]( mut: Mutable[ S ]) : S#Ref[ A ]
    def newID : S#ID
  }
}

trait Mutable[ S <: Sys[ S ]] {
   def id: S#ID
   protected final def newRef[ A ]( implicit tx: S#Tx ) : S#Ref[ A ] =
      tx.newRef[ A ]( this )

}

// Or maybe you could declare type Txn in the package object...
trait Txn[ S <: Sys[ S ]] extends Mutable.Txn[S]

object Foo {
  def newStruct[ S <: Sys[ S ]]( cross: Mutable[ S ])( implicit tx: S#Tx ) = 
    new Mutable[ S ] {
      val id        = tx.newID
      val allowed   = newRef[ Int ]
      val forbidden = tx.newRef[ Int ]( cross ) // doesn't compile...
    }
}
Другие вопросы по тегам