Optionality to ortogonalny problem dotyczący rodzaju danych. Więc tak, Option[Boolean]
ma tyle samo sensu, co Option[Int]
.
Porozmawiajmy o twoim konkretnym przypadku użycia.
Jeśli reguła biznesu mówi pola (powiedzmy) isPrimeTime
musi być typu Boolean
, ale nie jest obowiązkowe, to należy modelować go z Option[Boolean]
.
None
w tym kontekście wskazuje na brak wartości, Some(true)
oznaczałoby „obecny i prawdziwe”, Some(false)
oznaczałoby „obecny i fałsz”. Pozwoliłoby to również na dodawanie domyślne w kodzie jak poniżej:
val isPrimeTime = json.getAs[Option[Boolean]]("isPrimeTime").getOrElse(false)
Można stosować różne Option
kombinatorów innych przypadkach powszechnego użytku, które pojawiają się w takich ustawieniach. (Chciałbym dać pracę wyobraźni na ten temat.)
Jeśli domena ma dużo tych „trójstanowych” pól, a jeśli trzeci stan wskazuje na pewne specyficzne dla domeny pomysł, co nie wynika z kontekstu, Zdecydowanie radzę utworzyć własny rodzaj sum. Nawet jeśli możesz technicznie nadal używać Option[Boolean]
, może to nie być dobre dla twojego zdrowia psychicznego. Oto przykład.
sealed trait SignalValueIndication
case class Specified(value: Boolean) extends SignalValueIndication
case object DontCare extends SignalValueIndication
// Use
val signalValue = signalValueIndication match {
case Specified(value) => value
case DontCare => chooseOptimalSignalValueForImpl
}
To wydaje się jak byłoby to ogromne marnotrawstwo, ponieważ jesteś utraty kombinatorów dostępny na Option
. Częściowo dobrze. Częściowo dlatego, że ponieważ oba typy są izomorficzne, pisanie mostów nie byłoby tak trudne.
sealed trait SignalValueIndication {
// ...
def toOption: Option[Boolean] = this match {
case Specified(value) => Some(value)
case DontCare => None
}
def getOrElse(fallback: => Boolean) = this.toOption.getOrElse(fallback)
}
object SignalValueIndication {
def fromOption(value: Option[Boolean]): SignalValueIndication = {
value.map(Specified).getOrElse(DontCare)
}
}
To wciąż znacząca powielanie i trzeba oceniać indywidualnie w każdym przypadku-to-czy dodany klarowność nadrabia.
Podobna opinia została wydana przez HaskellTips na Twitterze niedawno i podobna dyskusja nastąpiła. Możesz go znaleźć here.
Czasami obecność lub brak pola jest decyzją, którą można zakodować w strukturze danych. Używanie Option
może być w takich przypadkach nieprawidłowe.
Oto przykład. Wyobraź sobie, że istnieje pole differentiator
, dla którego dozwolone są następujące dwa rodzaje wartości.
// #1
{ "type": "lov", "value": "somethingFromSomeFixedSet" },
// #2
{ "type": "text", "value": "foo", "translatable": false }
Tutaj zakładamy pole translatable
jest obowiązkowe, ale tylko dla ` "type": "tekst".
Można modelować go Option
tak:
case class Differentiator(
_type: DifferentiatorType,
value: String,
translatable: Option[Boolean]
)
jednak lepszy sposób na modelu, który będzie: Effective ML rozmowa
sealed trait Differentiator
case class Lov(value: String) extends Differentiator
case class Text(value: String, translatable: Boolean) extends Differentiator
Yaron Minsky zawiera sekcję w tej sprawie. Może się to okazać pomocne. (Chociaż używa ML do zilustrowania punktów, rozmowa jest całkiem dostępna i dotyczy również programistów Scala).
Oczywiście, jest to uzasadniona reprezentacja dla stanu trójstanowego: Prawdziwy, Fałszywy, Niewiedzać/Nie dbać. –
może nie jest optymalna pod względem wydajności (ponieważ jest to klasa w pełni rozwinięta), ale dlaczego nie? Jeśli chcesz zoptymalizować, możesz zakodować go jako bajt lub int, na przykład –