在 Hlist of Options 中无形地查找 Some among Nones 的实例
Shapeless find instance of Some among Nones in an Hlist of Options
假设我有以下 class 层次结构:
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
事实是,我比较 new 无形而且我不知道 ???
有所作为。
继续提问
有没有办法 "iterate" 通过 HList 并提取 Some
?
我明白,在笼统地说时,这是不可能的,如以下两个问题所示。 但是我想知道添加我上面设置的限制是否会有所不同
正如您在 link 中所解释的那样,如果您的值静态类型为 Some
和 None
,则只有在 HList
上才能执行此类操作,因此编译器可以做任何事情。
如果您有关于类型给出的额外信息(此处,恰好有一个选项可以是 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
是 None.type
的 HList
。让我们对正在寻找的 属性 做同样的事情:
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
的值。
编辑
举个例子:
val cat = Some(Cat(isFriendly = true)) :: None :: None :: HNil
IsOneSomeHList[Some[Cat] :: None.type :: None.type :: HNil].get(cat) == Cat(true)
假设我有以下 class 层次结构:
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
事实是,我比较 new 无形而且我不知道 ???
有所作为。
继续提问
有没有办法 "iterate" 通过 HList 并提取 Some
?
我明白,在笼统地说时,这是不可能的,如以下两个问题所示。 但是我想知道添加我上面设置的限制是否会有所不同
正如您在 link 中所解释的那样,如果您的值静态类型为 Some
和 None
,则只有在 HList
上才能执行此类操作,因此编译器可以做任何事情。
如果您有关于类型给出的额外信息(此处,恰好有一个选项可以是 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
是 None.type
的 HList
。让我们对正在寻找的 属性 做同样的事情:
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
的值。
编辑
举个例子:
val cat = Some(Cat(isFriendly = true)) :: None :: None :: HNil
IsOneSomeHList[Some[Cat] :: None.type :: None.type :: HNil].get(cat) == Cat(true)