Cách đây không lâu, tôi đã bắt đầu sử dụng Scala thay vì Java. Một phần của quá trình "chuyển đổi" giữa các ngôn ngữ đối với tôi là học cách sử dụng Eithers thay vì (đã kiểm tra) Exception. Tôi đã viết mã theo cách này được một thời gian, nhưng gần đây tôi bắt đầu tự hỏi liệu đó có thực sự là một cách tốt hơn để đi không.
Một lợi thế lớn Eithercó hơn Exceptionlà hiệu suất tốt hơn; một Exceptionnhu cầu để xây dựng một dấu vết ngăn xếp và đang bị ném. Theo như tôi hiểu, mặc dù, ném phần Exceptionkhông phải là đòi hỏi, nhưng xây dựng dấu vết ngăn xếp là.  
Nhưng sau đó, người ta luôn có thể xây dựng / kế thừa Exceptions scala.util.control.NoStackTrace, và thậm chí nhiều hơn thế, tôi thấy rất nhiều trường hợp trong đó bên trái của một Eitherthực tế là một Exception(cho phép tăng hiệu suất).
Một lợi thế khác Eitherlà an toàn trình biên dịch; trình biên dịch Scala sẽ không phàn nàn về các trình xử lý Exceptionkhông (không giống như trình biên dịch của Java). Nhưng nếu tôi không nhầm, quyết định này được đưa ra với cùng lý do đang được thảo luận trong chủ đề này, vì vậy ...
Về mặt cú pháp, tôi cảm thấy như Exceptionkiểu là rõ ràng hơn. Kiểm tra các khối mã sau (cả hai đều đạt được cùng chức năng):
Either Phong cách:
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 Phong cách:
@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 = ???Cái sau có vẻ sạch hơn đối với tôi và mã xử lý lỗi (khớp mẫu trên Eitherhoặc try-catch) khá rõ ràng trong cả hai trường hợp.
Vì vậy, câu hỏi của tôi là - tại sao sử dụng Eitherhơn (kiểm tra) Exception?
Cập nhật
Sau khi đọc câu trả lời, tôi nhận ra rằng tôi có thể đã thất bại trong việc trình bày cốt lõi của tình huống khó xử của mình. Mối quan tâm của tôi là không có việc thiếu các try-catch; một có thể "bắt" một Exceptionvới Try, hoặc sử dụng catchđể bọc các ngoại lệ với Left.
Vấn đề chính của tôi với Either/ Tryxuất hiện khi tôi viết mã có thể thất bại tại nhiều điểm trên đường đi; trong các kịch bản này, khi gặp phải một thất bại, tôi phải tuyên truyền sự thất bại đó trong toàn bộ mã của mình, do đó làm cho mã trở nên cồng kềnh hơn (như trong các ví dụ đã nói ở trên).
Thực sự có một cách khác để phá mã mà không cần Exceptions bằng cách sử dụng return(thực tế là một "điều cấm kỵ" khác trong Scala). Mã sẽ vẫn rõ ràng hơn so với Eithercách tiếp cận, và trong khi sạch hơn một chút so với Exceptionkiểu, sẽ không sợ Exceptions không bị bắt .
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))
  }
}Về cơ bản, các return Leftthay thế throw new Exceptionvà phương thức ngầm định rightOrReturnlà một phần bổ sung cho việc truyền ngoại lệ tự động lên ngăn xếp.
Try. Phần về Eithervs Exceptionchỉ đơn thuần nói rằng Eithers nên được sử dụng khi trường hợp khác của phương thức là "không đặc biệt". Đầu tiên, đây là một định nghĩa rất, rất mơ hồ. Thứ hai, nó có thực sự đáng bị phạt cú pháp không? Ý tôi là, tôi thực sự không phiền khi sử dụng Eithers nếu nó không dùng cho cú pháp mà họ trình bày.
                Eithertrông giống như một đơn nguyên đối với tôi. Sử dụng nó khi bạn cần các lợi ích thành phần chức năng mà các đơn vị cung cấp. Hoặc, có thể không .
                Eitherbản thân nó không phải là một đơn nguyên. Hình chiếu sang bên trái hoặc bên phải là một đơn nguyên, nhưng Eitherbản thân nó thì không. Bạn có thể làm cho nó trở thành một đơn nguyên, bằng cách "thiên vị" nó sang bên trái hoặc bên phải, mặc dù. Tuy nhiên, sau đó bạn truyền đạt một ngữ nghĩa nhất định trên cả hai mặt của một Either. Scala Eitherban đầu không thiên vị, nhưng gần đây đã bị thiên vị, vì vậy mà ngày nay, nó thực sự là một đơn nguyên, nhưng "sự đơn điệu" không phải là một tài sản vốn có Eithermà là kết quả của nó bị thiên vị.