Вопрос может быть немного запутанным, но намерение таково:
Я хотел бы ограничить значение другим значением в иерархии параметра типа. Учитывая, что они все типы, было бы здорово, если бы был применен аналогичный код.
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}")
}