scala - 如何强制子类型不包含超类型?
问题描述
实际问题:
让我们想象一下,一个景观屋的客户预订了一场音乐会。
一些音乐会门票有座位。
客户带来了配偶。限制:
1.客户的票和对应配偶的票要么都坐 ,要么 都没有坐。
如何在类型级别施加此限制?
我最初的想法是:
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)
)
有了这个,我想通过类型参数 A onConcertReservation[A]
强加 userTicket 和 SpearTicket 必须是相同的类型。
这样做允许编译器捕获上述违反限制的行为:
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)
)
有没有一种惯用的方法来实现我想要的?也许是某种“模式”?
谢谢,
解决方案
您可以设置Ticket
为 atrait
然后进行一些隐式类型检查。
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
接下来,您可以分别获取两个参数的类型,并包含一个隐式检查它们是否相等作为参数。您还可以添加类型不等式检查以确保该类型不是 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.
推荐阅读
- ios - 如何“弹出”导航控制器及其所有视图控制器
- react-native - React Native 自定义按钮样式问题
- asp.net - 从 ValuesController 中的 IdentityUser 访问自定义属性,无需扩展 - Web API
- javascript - 向css背景图片添加点击事件
- string - Dart:从包含数学运算符的用户输入字符串进行计算
- java - 通过 Eclipse IDE 运行程序参数
- unity3d - 为什么我的着色器在将代码从 ShaderToy 转换为 Unity 后显示为静态?
- c# - 在 C# WebAPI WebService 项目中执行 Python 代码
- node.js - 在节点上使用cheerio 修改后写入文件
- laravel - 加入多态关系,只返回一个结果