实际问题:
假设一家眼镜店的顾客预订了一场音乐会。
演唱会的一些票可坐。
客户带来配偶。
限制:
1. 两者都是客户的票证和相应配偶的票证是就座 OR 两者都是不坐。
如何在类型级别上施加此限制?
我最初的想法:
case class Ticket[S <: Option[String]](id: String, seat: S)
case class ConcertReservation[A <: Option[String]](userTicket: Ticket[A],
spouseTicket: Ticket[A])
val concertReservation =
ConcertReservation(
userTicket = Ticket(id = "id1", seat = Some("<seatId>")),
spouseTicket = Ticket(id = "id2", seat = None)
)
为此,我想通过ConcertReservation[A]
上的类型参数A将userTicket和spouseTicket设置为同一类型。
这样做可以使编译器捕获上述违反限制的情况:
Error:(12, 26) type mismatch;
found : .....Temp.Ticket[Some[String]]
required: .....Ticket[Option[String]]
Note: Some[String] <: Option[String], but class Ticket is invariant in type S.
You may wish to define S as +S instead. (SLS 4.5)
userTicket = Ticket(id = "id1", seat = Some("assad")),
但是有可能克服这一点。例如,下面的代码(可编译):
val concertReservation2: ConcertReservation[Option[String]] =
ConcertReservation(
userTicket = Ticket(id = "id1", seat = Some("assad")),
spouseTicket = Ticket(id = "id2", seat = None)
)
是否有惯用的方式来实现我想要的?也许是某种“模式”?
谢谢,
答案 0 :(得分:3)
您可以将Ticket
设置为trait
,然后进行一些隐式类型检查。
sealed trait Ticket{val id: String}
case class SeatedTicket(override val id: String, seat: String) extends Ticket
case class StandingTicket(override val id: String) extends Ticket
接下来,您可以分别获取两个参数的类型,并隐式检查它们是否等同于参数。您还可以添加类型不等式检查,以确保类型不是票证,但这将要求您包括诸如shapeless之类的库,或者对类型系统进行更多处理。
case class Reservation[T1 <: Ticket, T2 <: Ticket](user: T1, spouse: T2)(implicit ev: T1 =:= T2, ev2: T1 =:!= Ticket)
当T1和T2匹配时,它可以正常工作,但是当它们不同时,类型系统可以识别错误。
val sit1 = SeatedTicket("1","1A")
val sit2 = SeatedTicket("2","1B")
val stand1 = StandingTicket("3")
val stand2 = StandingTicket("4")
Reservation(sit1, sit2) //Runs fine
Reservation(stand1, stand2) //Runs fine
Reservation(sit1,stand1) //error: Cannot prove that SeatedTicket =:= StandingTicket.
答案 1 :(得分:2)
如果您从this answer复制install.packages("usmap")
library(usmap)
usmap::plot_usmap(include = c("HI"))
(“非相等类型”)的定义,则可以使用它来确定=!=
不是{{1} }:
A
这会导致预期的行为:
Option[String]
答案 2 :(得分:0)
据我对问题的理解,是否仅在运行时才知道是否分配了席位,对此您无法进行编译时检查。
但是,如果您确实想对两者都进行限制,则应该使用:
seats: Option[(String, String)]
,或者如果您想检查运行时,则可以在两个座位上进行一些模式匹配:
val valid = (userTicket.seat, spouseTicket.seat) match {
case (Some(_), Some(_)) | (None | None) => true
case _ => false
}