Çok uzun zaman önce Java yerine Scala kullanmaya başladım. Benim için diller arasındaki "dönüşüm" sürecinin bir Eitherkı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 Eitheredemediği Exceptiondaha iyi performans; bir Exceptionyığın izi oluşturmak için bir ihtiyaç ve atıldı. Anladığım kadarıyla, atmak Exceptionzor kısım değil, yığın izini oluşturmak.
Ama sonra, bir zaman / devralır inşa edebilir Exceptionler ile scala.util.control.NoStackTraceve bir sol tarafı nerede daha çok, ben durumlarda bol bkz Eitheraslında bir olduğunu Exception(performans artışı forgoing).
Bir diğer avantajı Eitherda 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 Eitherveya 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 Exceptionile Tryveya kullanmak catchile istisna sarmak için Left.
Ana sorun Either/ Tryyol 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 Exceptionkullanarak s olmadan kırmanın başka bir yolu var (aslında returnScala'da başka bir "tabu"). Kod hala Eitheryaklaşımdan daha açık ve Exceptionstilden 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 Leftikame throw new Exceptionve ikisinin üzerindeki örtük yöntem rightOrReturn, yığının otomatik istisnasının yayılması için bir tamamlayıcıdır.
Try. EitherVs ile ilgili bölüm Exceptionsadece Eitheryö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, Eithereğer sundukları sözdizimi yükü olmasaydı, s'yi kullanmakta sorun olmaz.
Eitherbana bir monad gibi geliyor. Monadların sağladığı fonksiyonel kompozisyon avantajlarına ihtiyacınız olduğunda kullanın. Ya da, belki değil .
Eithertek başına bir monad değil. Çıkıntı sol tarafında veya sağ tarafa bir tek hücreli, ama Eithertek 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 Eitherbaş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 Eitherdeğil, bunun önyargılı olmasının bir sonucudur.