Scala: ограничение параметра на основе значения параметра другого типа той же иерархии

Вопрос может быть немного запутанным, но намерение таково:

Я хотел бы ограничить значение другим значением в иерархии параметра типа. Учитывая, что они все типы, было бы здорово, если бы был применен аналогичный код.

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}")

}

person fracca    schedule 22.05.2014    source источник


Ответы (3)


Вы можете сделать каждый тип объекта 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)
person Dan Getz    schedule 22.05.2014

Вы можете использовать неявные:

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.

person Lee    schedule 22.05.2014

Используя ответы Майлза Сабина здесь, вы можете сделать:

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)
person Marth    schedule 22.05.2014