【问题标题】:Scala loses track of related types when concatenating (projecting) type members连接(投影)类型成员时,Scala 失去了对相关类型的跟踪
【发布时间】:2012-09-17 09:01:04
【问题描述】:

我是walking around a problem,我发现类型投影和抽象类型有一个新的奇怪问题。假设我有一个产生交易的系统,并且有一个我想桥接的对等系统。以下对我来说看起来不错:

trait Txn[S <: Sys[S]] {
  def peer: S#Peer#Tx
  def newID(): S#ID
  def newVar[A](id: S#ID, init: A): S#Var[A]
}
trait Sys[S <: Sys[S]] {
  type Tx <: Txn[S]
  type Peer <: Sys[Peer] // parallel system
  type Var[A]
  type ID
}

而且我可以使用直接系统:

def directWorks[S <: Sys[S]](implicit tx: S#Tx): Unit = {
  val id = tx.newID()
  val v  = tx.newVar(id, 0)
}

但不知何故,事务的peer方法存在缺陷,如下所示:

def indirectDoesnt[S <: Sys[S]](implicit tx: S#Tx): Unit = {
  val p   = tx.peer
  val id  = p.newID()
  val v   = p.newVar(id, 0) // what the **** - id is not compatible??
}

error: type mismatch;
 found   : id.type (with underlying type S#Peer#ID)
 required: _30129.Peer#ID where val _30129: S
          val v   = p.newVar(id, 0)
                             ^

我想变聪明并解决它:

def clever[S <: Sys[S]](implicit tx: S#Tx): Unit = {
  def directWorks[S <: Sys[S]](implicit tx: S#Tx): Unit = {
    val id = tx.newID()
    val v  = tx.newVar(id, 0)
  }
  directWorks(tx.peer)
}

...但这也失败了,提供了更多关于问题所在的线索:

error: inferred type arguments [S#Peer] do not conform to method
       directWorks's type parameter bounds [S <: Sys[S]]
          directWorks(tx.peer)
          ^

这一切都表明def peer: S#Peer#Tx 引入了一个问题,或者(更有可能?)type Peer &lt;: Sys[Peer] 在不用作类型 parameter 而是类型 member 时存在问题.

【问题讨论】:

    标签: scala types abstract-type type-projection bounded-quantification


    【解决方案1】:

    现在这是一个几乎可以接受的解决方案。它基于“修复”Sys 中的表示类型的想法;在具体的系统S中,我们总会有S#S#Tx == S#Tx等。我对这个解决方案的理解是,它将差异的责任从使用现场转移到系统本身。

    由于这涉及一些仪式,我仍然非常感谢有关删除或减少此仪式的其他答案;以及关于为什么这样做和省略对fix 的调用不起作用的解释。

    trait VarLike[ -Tx, A ] { def update( value: A )( implicit tx: Tx ) : Unit }
    
    trait Sys[ S <: Sys[ S ]] {
       type Tx       <: Txn[ S ]
       type Var[ A ] <: VarLike[ S#Tx, A ]
       type ID
       type Peer     <: Sys[ Peer ]
    
       // 'pop' the representation type ?!
       def fix[ A ]( v: S#Peer#Var[ A ]) : Peer#Var[ A ]
       def peer( tx: S#Tx ) : Peer#Tx
    }
    
    trait Txn[ S <: Sys[ S ]] {
       def newID() : S#ID
       def newVar[ A ]( id: S#ID, init: A ) : S#Var[ A ]
       def system: S
    }
    

    现在有两个示例系统。第一:

    class InMemTx( val system: InMem ) extends Txn[ InMem ] {
       def newID() {}
       def newVar[ A ]( id: InMem#ID, init: A ) : InMem#Var[ A ] =
          new VarLike[ InMemTx, A ] {
             def update( v: A )( implicit tx: InMemTx ) {}
          }
    }
    class InMem extends Sys[ InMem ] {
       type Tx       = InMemTx
       type Var[ A ] = VarLike[ Tx, A ]
       type ID       = Unit
       type Peer     = InMem  // reflect back to ourself
    
       def fix[ A ]( v: Var[ A ]) : Var[ A ] = v
       def peer( tx: Tx ) : Tx = tx
    }
    

    第二:

    class DurableTx( val system: Durable, val peer: InMem#Tx ) extends Txn[ Durable ] {
       def newID() = 33
       def newVar[ A ]( id: Durable#ID, init: A ) : Durable#Var[ A ] =
          new VarLike[ DurableTx, A ] {
            def update( v: A )( implicit tx: DurableTx ) {}
          }
    }
    class Durable extends Sys[ Durable ] {
       type Tx       = DurableTx
       type Var[ A ] = VarLike[ Tx, A ]
       type ID       = Int
       type Peer     = InMem
    
       def fix[ A ]( v: InMem#Var[ A ]) : InMem#Var[ A ] = v
       def peer( tx: Tx ) : InMem#Tx = tx.peer
    }
    

    并在使用现场验证:

    // let's make sure we can use the system as intended
    trait TestTrait[ S <: Sys[ S ]] {
       def v : S#Peer#Var[ Int ]
    
       def test( implicit tx: S#Tx ) {
          val s          = tx.system
          implicit val p = s.peer( tx )
          val vf         = s.fix( v ) // not cool...
          vf()            = 1
       }
    }
    
    // see if we can actually create variables
    class TestImpl[ S <: Sys[ S ]]( implicit tx: S#Tx ) extends TestTrait[ S ] {
       val v = {
          val s          = tx.system
          implicit val p = s.peer( tx )
          val id         = p.newID()
          p.newVar( id, 0 )
       }
    }
    

    【讨论】:

    • 而且它非常脆弱。例如tx.system 必须是val,不能是def,并且S#Peer 必须是Sys 不能是更具体的系统。你在任何地方改变一点点,整个事情就会崩溃。我已经花了几个小时试图让它全部工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-07
    相关资源
    最近更新 更多