这个问题可能有点令人困惑,但意图是:
我想将值限制为类型为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}")
}
答案 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])
然后您只能为Hedge
和D
创建具有不同类型的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)