I am looking for a way to restrict invocations of certain objects. Given a transactional system which defines a reference type, a transaction type, and an identifier:
trait Sys[S <: Sys[S]] {
type Ref[A]
type Tx <: Txn[S]
type ID
}
I would like to be able to mix in a trait that can be used to create mutable references:
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)
}
And the reference factory is defined as part of the transaction:
trait Txn[S <: Sys[S]] {
/* private[Mutable] */ def newRef[A](mut: Mutable[S]): S#Ref[A]
def newID: S#ID
}
Now, the problem is, in the following structure I can create references with false contexts:
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...
}
I would like to disallow the last call. Obviously, I cannot make newRef
in Txn
private to Mutable
, because Mutable
is not an enclosing class of Txn
. I also would like not to use package privacy, as one can easily break into newRef
by defining an object in that package.
Ideally I would like this:
trait Sys[S <: Sys[S]] { trait Mutable }
class MyStruct[S <: Sys[S]] extends S#Mutable { ... }
which would solve all the problems. But this is disallowed, since S
in S#Mutable
"is not a legal prefix for a constructor"
in scalac's universe....
Thanks for suggestions!
You can put the definition of Txn
in Mutable
's companion object, then make it private to Mutable
. Not sure if there are further implications, though.
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...
}
}