Frage Formlose Fundinstanz von "Einige unter Nonen" in einer Liste von Optionen


Nehmen wir an, ich habe die folgende Klassenhierarchie:

sealed trait Animal

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

Jetzt habe ich eine Instanz des Typs

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

Aber es gibt eine Einschränkung für die Instanz. Es kann nur eine der folgenden Formen sein

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

oder

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

oder

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

Entschuldigen Sie zuerst alle Inkohärenz - es ist Teil eines größeren Problems, das ich zu lösen versuche, das noch nicht gut artikuliert ist

Zweitens, die ??? ist nur mein erfundener Platzhalter für echte Instanz wie:

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

Die Sache ist, ich bin eher Neu zu formlos und ich weiß nicht genau, ob der Wert der ??? macht einen Unterschied.


Weiter zur Frage

Gibt es eine Möglichkeit, über den HList "iterieren" und extrahieren die Some?

Ich verstehe, dass, wenn ich allgemein spreche, es nicht möglich ist, wie in den folgenden zwei Fragen gezeigt. Aber Ich frage mich, ob das Hinzufügen der Einschränkungen, die ich oben gesetzt habe, einen Unterschied machen würde

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

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


5
2018-06-06 06:20


Ursprung


Antworten:


Wie in dem von Ihnen angegebenen Link erläutert, ist eine solche Operation nur möglich HList Wenn Ihre Werte statisch wie eingegeben sind Some und None, damit der Compiler etwas dagegen tun kann.

Wenn Sie zusätzliche Informationen haben, was der Typ gibt (hier die Tatsache, dass genau eine der Optionen sein kann) Some), bedeutet dies, dass Sie den falschen Typ verwenden, da Typen die Informationen sind, die Sie zum Zeitpunkt der Kompilierung auf die Werte haben. In diesem Fall ist der Typ, den Sie verwenden sollten Coproduct:

type AnimalCoproduct = Cat :+: Dog :+: Fish :+: CNil

val dog = Coproduct[AnimalCoproduct](Dog("brown"))

Nun zurück zu deiner Frage, vorausgesetzt, du weißt, was ist None und welche sind Some zur Kompilierzeit.

Zuerst müssen Sie überprüfen, welche HList Habe die Eigenschaft, dass sie eine Liste von sind 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] {}
}

So, jetzt, wenn es ein gibt implicit IsNoneList[L] im Umfang bedeutet das L ist ein HList von None.type. Machen wir dasselbe mit der Eigenschaft, die wir suchen:

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

Beachten Sie, dass wenn wir eine haben implicit IsOneSomeHList[L] im Umfang wissen wir das L hat die Eigenschaft, die wir wollen, aber wir können auch diese implizite verwenden, um den Typ und den Wert des einzigen zu erhalten Some In der Liste.

BEARBEITEN

Lassen Sie uns ein Beispiel geben:

val cat = Some(Cat(isFriendly = true)) :: None :: None :: HNil

IsOneSomeHList[Some[Cat] :: None.type :: None.type :: HNil].get(cat) == Cat(true)

5
2018-06-06 07:56