Çok uzun zaman önce Java yerine Scala kullanmaya başladım. Benim için diller arasındaki "dönüşüm" sürecinin bir Either
kısmı (işaretli) yerine s kullanmayı öğreniyordu Exception
. Bir süredir bu şekilde kod yazıyorum, ancak son zamanlarda bunun gerçekten daha iyi bir yol olup olmadığını merak etmeye başladım.
Önemli bir avantaj Either
edemediği Exception
daha iyi performans; bir Exception
yığın izi oluşturmak için bir ihtiyaç ve atıldı. Anladığım kadarıyla, atmak Exception
zor kısım değil, yığın izini oluşturmak.
Ama sonra, bir zaman / devralır inşa edebilir Exception
ler ile scala.util.control.NoStackTrace
ve bir sol tarafı nerede daha çok, ben durumlarda bol bkz Either
aslında bir olduğunu Exception
(performans artışı forgoing).
Bir diğer avantajı Either
da derleyici güvenliği; Scala derleyicisi, işlenmeyenlerden Exception
(Java derleyicisinin aksine) şikayet etmeyecektir . Ancak yanılmıyorsam, bu karar, bu konuda tartışılan aynı akıl ile gerekçelendirilir, bu yüzden ...
Sözdizimi açısından, Exception
-style çok daha net gibi hissediyorum . Aşağıdaki kod bloklarını inceleyin (her ikisi de aynı işlevi görür):
Either
stili:
def compute(): Either[String, Int] = {
val aEither: Either[String, String] = if (someCondition) Right("good") else Left("bad")
val bEithers: Iterable[Either[String, Int]] = someSeq.map {
item => if (someCondition(item)) Right(item.toInt) else Left("bad")
}
for {
a <- aEither.right
bs <- reduce(bEithers).right
ignore <- validate(bs).right
} yield compute(a, bs)
}
def reduce[A,B](eithers: Iterable[Either[A,B]]): Either[A, Iterable[B]] = ??? // utility code
def validate(bs: Iterable[Int]): Either[String, Unit] = if (bs.sum > 22) Left("bad") else Right()
def compute(a: String, bs: Iterable[Int]): Int = ???
Exception
stili:
@throws(classOf[ComputationException])
def compute(): Int = {
val a = if (someCondition) "good" else throw new ComputationException("bad")
val bs = someSeq.map {
item => if (someCondition(item)) item.toInt else throw new ComputationException("bad")
}
if (bs.sum > 22) throw new ComputationException("bad")
compute(a, bs)
}
def compute(a: String, bs: Iterable[Int]): Int = ???
İkincisi bana çok daha temiz görünüyor ve hatayı işleyen kod (her iki durumda da desen eşleştirme Either
veya try-catch
) oldukça açık.
Benim sorum şu - neden kullanım Either
(işaretli) Exception
?
Güncelleme
Cevapları okuduktan sonra ikilemin özünü sunamayabilirim. Benim endişe değil eksikliği try-catch
; bir ya "yakalama" bir can Exception
ile Try
veya kullanmak catch
ile istisna sarmak için Left
.
Ana sorun Either
/ Try
yol boyunca birçok noktada başarısız olabilir kod yazarken gelir; bu senaryolarda, bir hatayla karşılaştığımda, bu hatayı tüm kodum boyunca yaymak zorunda kaldım, böylece kodu (yukarıda belirtilen örneklerde gösterildiği gibi) daha hantal hale getirmeliyim.
Aslında kodu Exception
kullanarak s olmadan kırmanın başka bir yolu var (aslında return
Scala'da başka bir "tabu"). Kod hala Either
yaklaşımdan daha açık ve Exception
stilden biraz daha az temiz olsa da, yakalanmayanlardan korkmayacaktı Exception
.
def compute(): Either[String, Int] = {
val a = if (someCondition) "good" else return Left("bad")
val bs: Iterable[Int] = someSeq.map {
item => if (someCondition(item)) item.toInt else return Left("bad")
}
if (bs.sum > 22) return Left("bad")
val c = computeC(bs).rightOrReturn(return _)
Right(computeAll(a, bs, c))
}
def computeC(bs: Iterable[Int]): Either[String, Int] = ???
def computeAll(a: String, bs: Iterable[Int], c: Int): Int = ???
implicit class ConvertEither[L, R](either: Either[L, R]) {
def rightOrReturn(f: (Left[L, R]) => R): R = either match {
case Right(r) => r
case Left(l) => f(Left(l))
}
}
Temel olarak, return Left
ikame throw new Exception
ve ikisinin üzerindeki örtük yöntem rightOrReturn
, yığının otomatik istisnasının yayılması için bir tamamlayıcıdır.
Try
. Either
Vs ile ilgili bölüm Exception
sadece Either
yöntemin diğer örneği "istisnai değildir" olduğunda kullanılması gerektiğini belirtir . Birincisi, bu çok ama çok belirsiz bir tanım imho. İkincisi, sözdizimi cezasına gerçekten değer mi? Demek istediğim, Either
eğer sundukları sözdizimi yükü olmasaydı, s'yi kullanmakta sorun olmaz.
Either
bana bir monad gibi geliyor. Monadların sağladığı fonksiyonel kompozisyon avantajlarına ihtiyacınız olduğunda kullanın. Ya da, belki değil .
Either
tek başına bir monad değil. Çıkıntı sol tarafında veya sağ tarafa bir tek hücreli, ama Either
tek başına değil. Sen edebilir hale olsa sol tarafta veya sağ tarafa onu "polarizasyon" tarafından, bir monad. Ancak, o zaman her iki tarafına belirli bir semantik verirsiniz Either
. Scala'nın Either
başlangıçta tarafsız olduğu, ancak son zamanlarda önyargılı olduğu için, bugünlerde aslında bir monad, ancak "monadness", doğasında var olan bir özellik Either
değil, bunun önyargılı olmasının bir sonucudur.