Scala:根据同一层次结构的另一个类型参数值限制参数

时间:2014-05-22 11:00:21

标签: scala types type-bounds

这个问题可能有点令人困惑,但意图是:

我想将值限制为类型为paremeter的层次结构中的另一个值。鉴于它们都是类型,如果强制执行类似的代码,那将是很好的。

sealed trait Direction
case object Buy     extends Direction
case object Sell    extends Direction

case class Trade[D <: Direction](id: Long, itemId: Long, quantity: Int)

case class Hedge[D <: Direction, ??? -> O is D's OppositeDirection](id: Long, itemId: Long, openingTrade: Trade[D], closingTrade: Trade[O])  

简而言之:

val trade1 = Trade[Buy](id = 1, itemdId = 10, quantity = 100)
val trade2 = Trade[Sell](id = 2, itemdId = 10, quantity = -100)

val validHedge = Hedge[Buy, Sell](id = 563, itemId = 10, openingTrade= trade1, closingTrade = trade2)

是有效的,但我不应该被允许为不正确的配对创建对冲。例如:

val invalidHedge = Hedge[Buy, Sell](id = 563, itemId = 10, openingTrade= trade1, closingTrade = trade1)

任何建议都将不胜感激。

更新

如果在任何时候我希望我能在SO中接受多个答案,那就是这个时候了。所有答案都是有效的,质量很好。我将选择最能解决问题的那个和产生最易读错误的那个。所选择的答案具有相反的可用价值(肯定会派上用场)并且不需要暗示。它还会产生一个最清晰的错误消息。类型不等式非常优雅,但其中有一些神奇的元素,特别是它需要2个消歧。从代码中删除它会停止工作,没有明显的原因。隐含对当然是一个很好的解决方案,它与原始问题非常相似,但不像对立面那样有用且无意义。

感谢所有人给出的答案和快速反应。

完成后,以下是所有重新编写的代码:

object NotEquality {
  trait =!=[A, B]

  implicit def neq[A, B] : A =!= B = null
  implicit def neqAmbig1[A] : A =!= A = null
  implicit def neqAmbig2[A] : A =!= A = null //Without this duplicated line, the compiler will ignore the restriction and silently fail. Why?
}

object Trades {
  sealed trait Direction
  case object Buy     extends Direction
  case object Sell    extends Direction

  import NotEquality._
  case class Trade[D <: Direction](id: Long, itemId: Long, quantity: Int)
  case class Hedge[D <: Direction, O <: Direction](id: Long, itemId: Long, openingTrade: Trade[D], closingTrade: Trade[O])(implicit ev: D =!= O)
}


object TradesNeq extends App {
  import NotEquality._
  import Trades._

  val trade1 = Trade[Buy.type](1,10,100)
  val trade2 = Trade[Sell.type](2,10,-100)

  val validHedge = Hedge[Buy.type, Sell.type](id = 563, itemId = 10, openingTrade= trade1, closingTrade = trade2)

  println(s"Valid Hedge: ${validHedge}")
//  val invalidHedge = Hedge[Buy.type, Buy.type](563,10,trade1,trade1)
//  println(s"InvalidHedge ${invalidHedge}")
}


object TradesDOImpl extends App {
  sealed trait Direction
  case object Buy     extends Direction
  case object Sell    extends Direction

  class TradeDirection[D <: Direction, Op <: Direction]

  implicit val BuyToSell = new TradeDirection[Buy.type, Sell.type]
  implicit val SellToBuy = new TradeDirection[Sell.type, Buy.type]

  case class Trade[D <: Direction](id: Long, itemId: Long, quantity: Int)

  case class Hedge[D <: Direction, O <: Direction](id: Long, itemId: Long, openingTrade: Trade[D], closingTrade: Trade[O])(implicit d: TradeDirection[D, O])

  val trade1 = Trade[Buy.type](id = 1, itemId = 10, quantity = 100)
  val trade2 = Trade[Sell.type](id = 2, itemId = 10, quantity = -100)

  val validHedge = Hedge[Buy.type, Sell.type](id = 563, itemId = 10, openingTrade= trade1, closingTrade = trade2)
  println(s"Valid Hedge: ${validHedge}")
  val invalidHedge = Hedge[Buy.type, Buy.type](563,10,trade1,trade1)
  println(s"InvalidHedge ${invalidHedge}")
}


object TradesOpposite extends App {
    sealed trait Direction
    abstract class OppositeDirection[D <: Direction](val opposite: D) extends Direction
    abstract class Buy(opposite: Sell) extends OppositeDirection[Sell](opposite)
    case object Buy extends Buy(Sell)
    abstract class Sell(opposite: Buy) extends OppositeDirection[Buy](opposite)
    case object Sell extends Sell(Buy)

    case class Trade[D <: Direction](id: Long, itemId: Long, quantity: Int)

    case class Hedge[D <: Direction, O <: OppositeDirection[D]](id: Long, itemId: Long, openingTrade: Trade[D], closingTrade: Trade[O])

    val trade1 = Trade[Buy](id = 1, itemId = 10, quantity = 100)
    val trade2 = Trade[Sell](id = 2, itemId = 10, quantity = -100)

    val validHedge = Hedge[Buy, Sell](id = 563, itemId = 10, openingTrade= trade1, closingTrade = trade2)
    println(s"Valid Hedge: ${validHedge}")
//    val invalidHedge = Hedge[Buy, Buy](563,10,trade1,trade1)
//    println(s"InvalidHedge ${invalidHedge}")

}

3 个答案:

答案 0 :(得分:6)

您可以根据其Direction对应的类型对​​每个Direction对象的类型进行参数设置:

trait OppositeDirection[D <: Direction] extends Direction
trait Buy extends OppositeDirection[Sell]
case object Buy extends Buy
trait Sell extends OppositeDirection[Buy]
case object Sell extends Sell

然后你可以进行类型检查:

case class Hedge[D <: Direction, O <: OppositeDirection[D]](...)

更好的方法是让你的对象能够获得相反的对象:

abstract class OppositeDirection[D <: Direction](val opposite: D) extends Direction
abstract class Buy(opposite: Sell) extends OppositeDirection[Sell](opposite)
case object Buy extends Buy(Sell)
abstract class Sell(opposite: Buy) extends OppositeDirection[Buy](opposite)
case object Sell extends Sell(Buy)

答案 1 :(得分:5)

您可以使用隐含:

class TradeDirection[D <: Direction, Op <: Direction]

implicit val BuyToSell = new TradeDirection[Buy.type, Sell.type]
implicit val SellToBuy = new TradeDirection[Sell.type, Buy.type]

case class Hedge[D <: Direction, O <: Direction](id: Long, itemId: Long, openingTrade: Trade[D], closingTrade: Trade[O])(implicit d: TradeDirection[D, O])

然后您只能为HedgeD创建具有不同类型的O个实例。

答案 2 :(得分:3)

使用Miles Sabin回答here,你可以这样做:

trait =!=[A, B]

implicit def neq[A, B] : A =!= B = null
implicit def neqAmbig1[A] : A =!= A = null
implicit def neqAmbig2[A] : A =!= A = null

sealed trait Direction
case object Buy     extends Direction
case object Sell    extends Direction
type Buy = Buy.type
type Sell = Sell.type

case class Trade[D <: Direction](id: Long, itemId: Long, quantity: Int)
case class Hedge[D <: Direction, O <: Direction](id: Long, itemId: Long, openingTrade: Trade[D], closingTrade: Trade[O])(implicit ev: D =!= O)

val trade1 = Trade[Buy.type](1,10,100)
val trade2 = Trade[Sell.type](2,10,-100)

给你:

scala> val validHedge = Hedge[Buy, Sell](563,10,trade1,trade2)
validHedge: Hedge[Buy.type,Sell.type] = Hedge(563,10,Trade(1,10,100),Trade(2,10,-100))

scala> val invalidHedge = Hedge[Buy, Buy](563,10,trade1,trade1)
<console>:1: error: ambiguous implicit values:
 both method neqAmbig1 of type [A]=> =!=[A,A]
 and method neqAmbig2 of type [A]=> =!=[A,A]
 match expected type =!=[Buy,Buy]
       val validHedge = Hedge[Buy, Buy](563,10,trade1,trade1)