Shapeless find an instance of Some among Nones in the list of options

lets say that I have the following class hierarchy:

sealed trait Animal case class Cat(isFriendly: Boolean) extends Animal case class Dog(color: String) extends Animal case class Fish(isFreshWater: Boolean) extends Animal 

I now have an instance of type

 Option[Cat] :: Option[Dog] :: Option[Fish] :: HNil 

But there is a limitation on the instance. It can only be one of the following forms

 Some(Cat(???)) :: None :: None :: HNil 

or

 None :: Some(Dog(???)) :: None :: HNil 

or

 None :: None :: Some(Fish(???)) :: HNil 

First, excuse any inconsistency - this is part of a larger problem that I am trying to solve, which has not yet been formulated.

Secondly, ??? - this is just my inventive place holder for a real instance, for example:

 None :: Some(Dog(brown)) :: None :: HNil 

The thing is, I’m rather new formless, and I don’t know for sure whether it matters ??? .


Forward to the question

Is there a way to iterate over an HList and extract Some ?

I understand that, broadly speaking, this is not possible, as shown in the following two questions. But I wonder if adding the restrictions set above will make a difference.

stack overflow

stack overflow

+5
source share
1 answer

As explained in the link you pointed out, such an operation is only possible on HList if your values ​​are statically entered as Some and None , so the compiler can do something about it.

If you have additional information about what the type gives (here the fact that exactly one of the parameters may be Some ), this means that you are using the wrong type, since types are the information that you have about the values ​​during compilation. In this case, the type you should use is Coproduct :

 type AnimalCoproduct = Cat :+: Dog :+: Fish :+: CNil val dog = Coproduct[AnimalCoproduct](Dog("brown")) 

Now back to your question, assuming that you know what is None and which are Some at compile time.

First you need to check which HList has the property that they are a None list.

 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] {} } 

So, if there is implicit IsNoneList[L] , this means that L is HList of None.type . Let me do the same with the property we are looking for:

 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) } } 

Note that if we have implicit IsOneSomeHList[L] in scope, we know that L has the property we want, but we can also use this implicit to get the type and value of the only Some in the list.

EDIT

Here is an example:

 val cat = Some(Cat(isFriendly = true)) :: None :: None :: HNil IsOneSomeHList[Some[Cat] :: None.type :: None.type :: HNil].get(cat) == Cat(true) 
+5
source

Source: https://habr.com/ru/post/1268579/


All Articles