假设我有以下类层次结构:
sealed trait Animal
case class Cat(isFriendly: Boolean) extends Animal
case class Dog(color: String) extends Animal
case class Fish(isFreshWater: Boolean) extends Animal
现在我有一个类型为
的实例Option[Cat] :: Option[Dog] :: Option[Fish] :: HNil
但实例有限制。它只能是以下形式之一
Some(Cat(???)) :: None :: None :: HNil
或
None :: Some(Dog(???)) :: None :: HNil
或
None :: None :: Some(Fish(???)) :: HNil
首先,请原谅任何不连贯性 - 这是我试图解决的一个尚未明确表达的更大问题的一部分
其次,???
只是我为实例设计的占位符,例如:
None :: Some(Dog(brown)) :: None :: HNil
事实是,我宁愿 新 无形,我也不确切知道???
的价值是否有所作为。< / p>
转到问题
有没有办法去&#34;迭代&#34;在HList上并提取Some
?
据我所知,一般说来就不可能如以下两个问题所示。 但是我想知道添加上面设置的限制是否会产生影响
答案 0 :(得分:5)
正如您在链接中所解释的那样,如果您的值静态类型为HList
和Some
,则只能在None
上执行此类操作,以便编译器可以执行任何操作它
如果您有其他类型的信息(此处,其中一个选项可能是Some
),则表示您使用了错误的类型,因为类型是您在编译时获得的有关值的信息。在这种情况下,您应该使用的类型是Coproduct
:
type AnimalCoproduct = Cat :+: Dog :+: Fish :+: CNil
val dog = Coproduct[AnimalCoproduct](Dog("brown"))
现在,回到你的问题,假设你知道哪些是None
,哪些是Some
在编译时。
首先,您需要检查哪个HList
具有属于None
列表的属性。
trait IsNoneList[L <: HList]
object IsNoneList {
//all values in an HNil are None, since there aren't any
implicit val hnil: IsNoneList[HNil] = new IsNoneList[HNil] {}
//if all the values in the tail are None, and the head is None, then all the values are None
implicit def hcons[T <: HList: IsNoneList]: IsNoneList[None.type :: T] = new IsNoneList[None.type :: T] {}
}
现在,如果范围内有implicit IsNoneList[L]
,则表示L
是HList
None.type
。让我们对我们正在寻找的财产做同样的事情:
trait IsOneSomeHList[L <: HList] {
type OneSome
def get(l: L): OneSome
}
object IsOneSomeHList {
type Aux[L <: HList, O] = IsOneSomeHList[L] { type OneSome = O }
def apply[L <: HList](implicit L: IsOneSomeHList[L]) = L
// if the tail is full of None, and the head is a Some, then the property is true
implicit def someHead[A, T <: HList: IsNoneList]: Aux[Some[A] :: T, A] = new IsOneSomeHList[Some[A] :: T] {
type OneSome = A
def get(l: Some[A] :: T) = l.head.get
}
//if the head is None, and the tail has the property, then the HCons also has the property, with the same extraction function
implicit def noneHead[T <: HList](implicit T: IsOneSomeHList[T]): Aux[None.type :: T, T.OneSome] = new IsOneSomeHList[None.type :: T] {
type OneSome = T.OneSome
override def get(l: ::[None.type, T]): T.OneSome = T.get(l.tail)
}
}
请注意,如果我们在范围内有implicit IsOneSomeHList[L]
,我们知道L
具有我们想要的属性,但我们也可以使用此隐式来获取唯一Some
的类型和值。列表中的1}}。
修改强>
让我们举个例子:
val cat = Some(Cat(isFriendly = true)) :: None :: None :: HNil
IsOneSomeHList[Some[Cat] :: None.type :: None.type :: HNil].get(cat) == Cat(true)