选项清单中的Nones中的一些无形查找实例

时间:2017-06-06 06:20:06

标签: scala shapeless hlist

假设我有以下类层次结构:

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

据我所知,一般说来就不可能如以下两个问题所示。 但是我想知道添加上面设置的限制是否会产生影响

https://stackoverflow.com/a/28598157/101715

https://stackoverflow.com/a/29572541/101715

1 个答案:

答案 0 :(得分:5)

正如您在链接中所解释的那样,如果您的值静态类型为HListSome,则只能在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],则表示LHList 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)