π Chapter 4: μμΈλ₯Ό μ΄μ©νμ§ μμ μ€λ₯ μ²λ¦¬
μμΈλ₯Ό λμ§λ κ²μ΄ νλμ λΆμ ν¨κ³Όμμ κ°λ¨ν μΈκΈνλ€. μ€ν¨ μν©κ³Ό μμΈλ₯Ό 보ν΅μ κ°μΌλ‘ ννν μ μμΌλ©°, μΌλ°μ μΈ μ€λ₯ μ²λ¦¬, 볡ꡬ ν¨ν΄μ μΆμνν κ³ μ°¨ ν¨μλ₯Ό μμ±ν μ μλ€λ κ²μ΄λ€. μ€λ₯λ₯Ό κ°μΌλ‘μ λλ €μ€λ€λ ν¨μμ ν΄λ²μ λ μμ νκ³ μ°Έμ‘° ν¬λͺ μ±μ μ μ§νλ€λ μ₯μ μ΄ μλ€. κ³λ€κ° κ³ μ°¨ ν¨μ λλΆμ μμΈμ μ£Όλ μ΄μ μΈ μ€λ₯ μ²λ¦¬ λ ΌλΌμ ν΅ν©λ μ μ§λλ€.
π μμΈμ μ₯λ¨μ β
μμΈκ° μ μ°Έμ‘° ν¬λͺ μ±μ ν΄μΉ κΉ? κ·Έλ¦¬κ³ κ·Έκ²μ΄ μ λ¬Έμ κ° λ κΉ?
// μμΈλ₯Ό λμ§κ³ λ°κΈ°
def failingFn(i: Int): Int = {
val y: Int = throw new Exception("fail!")
try {
val x = 42 + 5
x + y
}
catch { case e: Exception => 43 }
}
// μμλλ‘ μ€λ₯ λ°μ
y
κ° μ°Έμ‘°μ ν¬λͺ
νμ§ μμμ μ¦λͺ
ν μ μλ€. μμμ μ°Έμ‘° ν¬λͺ
ννμμ κ·Έκ²μ΄ μ§μΉνλ κ°μΌλ‘ μΉνν΄λ νλ‘κ·Έλ¨μ μλ―Έκ° λ³νμ§ μλλ€λ μ μ κΈ°μ΅ν κ²μ΄λ€. λ§μΌ x + y
μ y
λ₯Ό throw new Exception("fail!")
λ‘ μΉννλ©΄ κ·Έμ κ³Όλ λ€λ₯Έ κ²°κ³Όκ° λμ¨λ€. μ΄μ λ μμΈλ₯Ό μ‘μμ 43μ λλ €μ£Όλ try
λΈλ‘ μμμ μμΈκ° λ°μνκΈ° λλ¬Έμ΄λ€.
def failingFn2(i: Int): Int = {
try {
val x = 42 + 5
x + ((throw new Exception("fail!")): Int)
}
catch { case e: Exception => 43 }
}
// 43
μ°Έμ‘° ν¬λͺ μ±μ΄λΌλ κ²μ, μ°Έμ‘°μ ν¬λͺ ν ννμμ μλ―Έλ λ¬Έλ§₯(context)μ μμ‘΄νμ§ μμΌλ©° μ§μμ μΌλ‘ μΆλ‘ ν μ μμ§λ§ μ°Έμ‘°μ ν¬λͺ νμ§ μμ ννμμ μλ―Έλ λ¬Έλ§₯μ μμ‘΄μ μ΄κ³ μ’ λ μ μμ μΆλ‘ μ΄ νμνλ€λ κ²μΌλ‘ μ΄ν΄ν΄λ λ κ²μ΄λ€.
μμΈμ μ£Όλ λ¬Έμ λ κ°μ§λ λ€μκ³Ό κ°λ€.
- λ°©κΈ λ Όμνλ―μ΄, μμΈλ μ°Έμ‘° ν¬λͺ μ±μ μλ°νκ³ λ¬Έλ§₯ μμ‘΄μ±μ λμ νλ€. λ°λΌμ μΉν λͺ¨νμ κ°λ¨ν μΆλ‘ μ΄ λΆκ°λ₯ν΄μ§κ³ μμΈμ κΈ°μ΄ν νΌλμ€λ¬μ΄ μ½λκ° λ§λ€μ΄μ§λ€. μ΄κ²μ΄ μμΈλ₯Ό μ€λ₯ μ²λ¦¬μλ§ μ¬μ©νκ³ νλ¦μ μ μ΄μλ μ¬μ©νμ§ λ§μμΌ νλ€λ μμ€μ κ·Όμμ΄λ€.
- μμΈλ νμμ μμ νμ§ μλ€.
failingFn
μ νμμΈInt => Int
λ§ λ³΄κ³ λ μ΄ ν¨μκ° μμ€λ₯Ό λμ§ μ μλ€λ μ¬μ€μ μ ν μ μ μμΌλ©°, κ·Έλμ μ»΄νμΌλ¬λfailingFn
μ νΈμΆμμκ² κ·Έ μμΈλ€μ μ²λ¦¬νλ λ°©μμ κ²°μ νλΌκ³ κ°μ ν μ μλ€.
μ΄λ° λ¨μ λ€μ΄ μμΌλ©΄μλ μμΈμ κ°μ₯ μ₯μ μΈ μ€λ₯ μ²λ¦¬ λ Όλ¦¬μ ν΅ν©κ³Ό μ€μμ§μ€νλ₯Ό μ μ§νλ λμμ΄ μμΌλ©΄ μ’μ κ²μ΄λ€. μ§κΈλΆν° μκ°νλ λμ κΈ°λ²μ "μμΈλ₯Ό λμ§λ λμ , μμΈμ μΈ μ‘°κ±΄μ λ°μνμμ λ»νλ κ°μ λλ €μ€λ€"λΌλ μ€λλ μ°©μμ κΈ°μ΄νλ€. λ¨, μ΄ κΈ°λ²μμλ μ€λ₯ λΆνΈλ₯Ό μ§μ λλ €μ£Όλ λμ κ·Έλ° '미리 μ μν΄ λ μ μλ κ°λ€'μ λννλ μλ‘μ΄ μΌλ°μ νμμ λμ νκ³ , μ€λ₯μ μ²λ¦¬μ μ νμ κ΄ν 곡ν΅μ μΈ ν¨ν΄λ€μ κ³ μ°¨ ν¨μλ€μ μ΄μ©ν΄μ μΊ‘μννλ€. μ°λ¦¬κ° μ¬μ©νλ μ€λ₯ μ²λ¦¬ μ λ΅μ νμμ μμ ν μμ νλ©°, μ΅μνμ ꡬ문μ μ‘μμΌλ‘λ μ€μΉΌλΌμ νμ μ κ²κΈ°λ₯Ό λμμ λ°μμ μ€μλ₯Ό 미리 λ°κ²¬ν μ μλ€.
π μμΈμ κ°λ₯ν λμλ€β
μμΈ λμ μ¬μ©ν λ§ν μ¬λ¬ κ°μ§ μ κ·Όλ°©μμ μ‘°μ¬ν΄λ³΄μ. λ€μμ λͺ©λ‘μ νκ· μ κ³μ°νλ ν¨μμ΄λ€. λΉ λͺ©λ‘μ λν΄μλ νκ· μ΄ μ μλμ§ μλλ€.
def mean(xs: Seq[Double]): Double =
if (xs.isEmpty)
throw new ArithmeticException("mean of empty list!")
else xs.sum / xs.length
mean
μ λν΄ μμΈ λμ μ¬μ©ν μ μλ λμ λͺ κ°μ§λ₯Ό μ΄ν΄λ³΄μ.
첫 λ²μ§Έ λμμ Double
νμμ κ°μ§ κ°μ λλ €μ£Όλ κ²μ΄λ€. λͺ¨λ κ²½μ°μ κ·Έλ₯ xs.sum / xs.length
λ₯Ό λλ €μ€λ€λ©΄ λΉ λͺ©λ‘μ λν΄μλ 0.0 / 0.0
μ λλ €μ£Όκ² λλλ°, μ΄λ Double.NaN
μ΄λ€. μλλ©΄ λ€λ₯Έ μ΄λ€ κ²½κ³ κ°μ λλ €μ€ μλ μκ² λ€. μν©μ λ°λΌμλ μνλ νμμ κ° λμ null
μ λλ €μ€ μλ μλ€. μ΄λ° λΆλ₯μ μ κ·Όλ°©μμ μμΈ κΈ°λ₯μ΄ μλ μΈμ΄μμ μ€λ₯λ₯Ό μ²λ¦¬νλ λ° νν μ°μΈλ€. κ·Έλ¬λ μ΄ μ±
μμλ μ΄λ° μ κ·Όλ°©μμ κ±°λΆνλ€. μ΄μ λ μ¬λ¬ κ°μ§μ΄λ€.
- μ€λ₯κ° μ리 μμ΄ μ νλ μ μλ€.
- μ€μμ μ¬μ§κ° λ§λ€λ μ μΈμ, νΈμΆνλ μͺ½μ νΈμΆμκ° 'μ§μ§' κ²°κ³Όλ₯Ό λ°μλμ§ μ κ²νλ λͺ
μμ
if
λ¬Έλ€λ‘ ꡬμ±λ νμ λ°ν μ½λκ° μλΉν λμ΄λλ€. - λ€νμ μ½λμλ μ μ©ν μ μλ€. μΆλ ₯ νμμ λ°λΌμλ κ·Έ νμμ κ²½κ³ κ°μ κ²°μ νλ κ²μ΄ λΆκ°λ₯ν μλ μλ€.
- νΈμΆμμκ² νΉλ³ν λ°©μΉ¨μ΄λ νΈμΆ κ·μ½μ μꡬνλ€.
mean
ν¨μλ₯Ό μ λλ‘ μ¬μ©νλ €λ©΄ νΈμΆμκ° κ·Έλ₯mean
μ νΈμΆν΄μ κ·Έ κ²°κ³Όλ₯Ό μ¬μ©νλ κ² μ΄μμ μμ μ μνν΄μΌ νλ€. ν¨μμ μ΄λ° νΉλ³ν λ°©μΉ¨μ λΆμ¬νλ©΄, λͺ¨λ μΈμλ₯Ό κ· μΌν λ°©μμΌλ‘ μ²λ¦¬ν΄μΌ νλ κ³ μ°¨ ν¨μμ μ λ¬νκΈ° μ΄λ €μμ§λ€.
λ λ€λ₯Έ λμμ ν¨μκ° μ λ ₯μ μ²λ¦¬ν μ μλ μν©μ μ²νμ λ 무μμ ν΄μΌ νλμ§ λ§ν΄μ£Όλ μΈμλ₯Ό νΈμΆμκ° μ§μ νλ κ²μ΄λ€.
def mean_1(xs: IndexedSeq[Double], onEmpty: Double): Double =
if (xs.isEmpty) onEmpty
else xs.sum / xs.length
μ΄λ κ² νλ©΄ mean
μ λΆλΆ ν¨μκ° μλ μμ ν¨μκ° λλ€. κ·Έλ¬λ μ¬κΈ°μλ κ²°κ³Όκ° μ μλμ§ μμ κ²½μ°μ μ²λ¦¬ λ°©μμ ν¨μμ μ§μ μ μΈ νΈμΆμκ° μκ³ μμ΄μΌ νκ³ κ·Έλ° κ²½μ°μλ νμ νλμ Double
κ°μ λλ €μ£Όμ΄μΌ νλ€λ λ¨μ μ΄ μλ€.
μ°λ¦¬μκ² νμν κ²μ, μ μλμ§ μμ κ²½μ°κ° κ°μ₯ μ λΉν μμ€μμ μ²λ¦¬λλλ‘ κ·Έ μ²λ¦¬ λ°©μμ κ²°μ μ λ―Έλ£° μ μκ² νλ λ°©λ²μ΄λ€.
π Option μλ£ νμβ
ν΄λ²μ, ν¨μκ° νμ λ΅μ λ΄μ§ λͺ»νλ€λ μ μ λ°ν νμμ ν΅ν΄μ λͺ
μμ μΌλ‘ νννλ κ²μ΄λ€. μ΄λ₯Ό, μ€λ₯ μ²λ¦¬ μ λ΅μ νΈμΆμμκ² λ―Έλ£¨λ κ²μΌλ‘ μκ°ν΄λ λλ€. μ΄λ₯Ό μν΄ Option
μ΄λΌλ μλ‘μ΄ νμμ λμ
νλ€.
sealed trait Option[+A]
case class Some[+A](get: A) extends Option[A]
case object None extends Option[Nothing]
Option
μλ λ κ°μ κ²½μ° λ¬Έμ΄ μλ€. Option
μ μ μν μ μλ κ²½μ°μλ Some
μ΄ λκ³ , μ μν μ μλ κ²½μ°μλ None
μ΄ λλ€.
μ΄μ Option
μ μ΄μ©ν΄μ mean
μ ꡬννλ©΄ λ€μκ³Ό κ°μ μ½λκ° λλ€.
def mean(xs: Seq[Double]): Option[Double] =
if (xs.isEmpty) None
else Some(xs.sum / xs.length)
μ΄μ λ μ΄ ν¨μμ κ²°κ³Όκ° νμ μ μλμ§ μλλ€λ μ¬μ€μ΄ ν¨μμ λ°ν λ°©μμ λ°μλμ΄ μλ€. ν¨μκ° νμ μ μΈλ λ°ν νμμ κ²°κ³Όλ₯Ό λλ €μ£Όμ΄μΌ νλ€λ μ μ μ¬μ νλ―λ‘, mean
μ μ΄μ νλμ μμ ν¨μμ΄λ€. μ΄ ν¨μλ μ
λ ₯ νμμ λͺ¨λ κ°μ λν΄ μ νν νλμ μΆλ ₯ νμ κ°μ λλ €μ€λ€.
π Optionμ μ¬μ© ν¨ν΄β
λΆλΆ ν¨μλ νλ‘κ·Έλλ°μμ νν λ³Ό μ μμΌλ©°, FPμμλ κ·Έλ° λΆλΆμ±μ νν Option
κ°μ μλ£ νμμΌλ‘ μ²λ¦¬νλ€.
Option
μ΄ νΈλ¦¬ν μ΄μ λ, μ€λ₯ μ²λ¦¬μ κ³΅ν΅ ν¨ν΄μ κ³ μ°¨ ν¨μλ€μ μ΄μ©ν΄μ μΆμΆν¨μΌλ‘μ¨ μμΈ μ²λ¦¬ μ½λμ νν μλ°λλ νμ λ°ν μ½λλ₯Ό μμ±νμ§ μμλ λλ€λ μ μ΄λ€.
Optionμ λν κΈ°λ³Έμ μΈ ν¨μλ€β
Option
μ μ΅λ νλμ μμλ₯Ό λ΄μ μ μλ€λ μ μ μ μΈνλ©΄ List
μ λΉμ·νλ€.
// Option μλ£ νμ
trait Option[+A] {
// λ§μΌ Optionμ΄ Noneμ΄ μλλ©΄ fλ₯Ό μ μ©
def map[B](f: A => B): Option[B]
// λ§μΌ Optionμ΄ Noneμ΄ μλλ©΄ f(μ€ν¨ν μ μμ)λ₯Ό μ μ©νλ€.
def flatMap[B](f: A => Option[B]): Option[B]
// B >: Aλ B νμ 맀κ°λ³μκ° λ°λμ Aμ μμνμμ΄μ΄μΌ ν¨μ μλ―Ένλ€.
def getOrElse[B >: A](default: => B): B
// obλ νμν κ²½μ°μλ§ νκ°νλ€.
def orElse[B >: A](ob: => Option[B]): Option[B]
// κ°μ΄ fλ₯Ό λ§μ‘±νμ§ μμΌλ©΄ Someμ NoneμΌλ‘ λ³ννλ€.
def filter(f: A => Boolean): Option[A]
}
κΈ°λ³Έμ μΈ Option ν¨μλ€μ μ©λ‘β
νλμ Option
μ λν΄ λͺ
μμ μΈ ν¨ν΄ λΆν¨μ μ μ©ν μλ μμ§λ§, κ±°μ λͺ¨λ κ²½μ°μ μμ λ§ν κ³ μ°¨ ν¨μλ€μ μ¬μ©νκ² λλ€.
map
ν¨μλ Option
μμ κ²°κ³Όλ₯Ό λ³ννλ λ° μ¬μ©ν μ μλ€. μ΄λ₯Ό μ€λ₯κ° λ°μνμ§ μμλ€λ κ°μ νμμ κ³μ°μ μ§ννλ κ²μΌλ‘ μκ°ν΄λ λ κ²μ΄λ€. λν, μ΄λ μ€λ₯ μ²λ¦¬λ₯Ό λμ€μ μ½λμ 미루λ μλ¨μ΄κΈ°λ νλ€.
case class Employee(name: String, department: String)
def lookupByName(name: String): Option[Employee] = ...
val joeDepartment: Option[String] =
lookupByName("Joe").map(_.department)
μ΄ μμμ lookupByName("Joe")
λ Option[Employee]
λ₯Ό λλ €μ€λ€. κ·Έκ²μ map
μΌλ‘ λ³ννλ©΄ Joe
κ° μν λΆμμ μ΄λ¦μ λ»νλ Option[String]
μ΄ λμ¨λ€. μ¬κΈ°μ lookupBtName("Joe")
μ κ²°κ³Όλ₯Ό λͺ
μμ μΌλ‘ μ κ²νμ§ μμμ μ£Όλͺ©νκΈ° λ°λλ€. κ·Έλ₯ μ€λ₯κ° μ ν λ°μνμ§ μμλ€λ λ―μ΄ map
μ μΈμ μμμ κ³μ°μ κ³μ μ§ννλ€. λ§μΌ lockupByName("Joe")
κ° None
μ λλ €μ£Όμλ€λ©΄ κ³μ°μ λλ¨Έμ§ λΆλΆμ΄ μ·¨μλμ΄μ map
μ _.department
ν¨μλ₯Ό μ ν νΈμΆνμ§ μλλ€.
flatMap
μ μ΄μ©νλ©΄ μ¬λ¬ λ¨κ³λ‘ μ΄λ£¨μ΄μ§ κ³μ°μ μννλ μ΄λ€ λ¨κ³λΌλ μ€ν¨νλ©΄ κ·Έ μ¦μ λλ¨Έμ§ λͺ¨λ κ³Όμ μ΄ μ·¨μλλ λ°©μμΌλ‘ μνν μ μλ€. μ΄λ None.flatMap(f)
κ° f
λ₯Ό μ€ννμ§ μκ³ μ¦μ None
μ λλ €μ£ΌκΈ° λλ¬Έμ΄λ€.
filter
λ μ±κ³΅μ μΈ κ°μ΄ μ£Όμ΄μ§ μ μ΄μ λΆν©νμ§ μμ λ μ±κ³΅μ μ€ν¨λ‘ λ³ννλλ° μ¬μ©ν μ μλ€. νν μ¬μ© ν¨ν΄μ map
, flatMap
, filter
μ μμμ μ‘°ν©μ μ΄μ©ν΄μ Option
μ λ³ννκ³ μ μΌ λμμ getOrElse
λ₯Ό μ΄μ©ν΄μ μ€λ₯ μ²λ¦¬λ₯Ό μννλ κ²μ΄λ€.
val dept: String =
lookupByName("Joe").
map(_.dept).
filter(_ != "Accounting").
getOrElse("Default Dept")
μ¬κΈ°μ getOrElse
λ Option[String]
μ String
μΌλ‘ λ³ννλ "Joe"
λΌλ ν€κ° Map
μ μ‘΄μ¬νμ§ μκ±°λ Joe
μ λΆμκ° "Accounting"
μΈ κ²½μ°μλ κΈ°λ³Έ λΆμ μ΄λ¦μ λλ €μ£Όλ μν μ νλ€.
μ€λ₯λ₯Ό 보ν΅μ κ°μΌλ‘ λλ €μ£Όλ©΄ μ½λλ₯Ό μ§κΈ°κ° νΈν΄μ§λ©°, κ³ μ°¨ ν¨μλ₯Ό μ¬μ©ν¨μΌλ‘μ¨ μμΈμ μ£Όλ μ₯μ μΈ μ€λ₯ μ²λ¦¬ λ
Όλ¦¬μ ν΅ν©κ³Ό 격리λ μ μ§ν μ μλ€. κ³μΌμ 맀 λ¨κ³λ§λ€ None
μ μ κ²ν νμκ° μμμ μ£Όλͺ©νκΈ° λ°λλ€.
π μμΈ μ§ν₯μ APIμ Option ν©μ±κ³Ό μΉκΈ, κ°μΈκΈ°β
μΌλ¨ Option
μ μ¬μ©νκΈ° μμνλ©΄ μ½λ κΈ°λ° μ 체μ Option
μ΄ λ²μ§κ² λ리λΌλ μ±κΈν κ²°λ‘ μ λ΄λ¦¬λ λ
μλ μμ κ²μ΄λ€. μ¦, Option
μ λ°κ±°λ λλ €μ£Όλ λ©μλλ₯Ό νΈμΆνλ λͺ¨λ μ½λλ₯Ό Some
μ΄λ None
μ μ²λ¦¬νλλ‘ μμ ν΄μΌ νλ€κ³ μΆμΈ‘ν μ μλ€. κ·Έλ¬λ μ€μ λ‘λ κ·Έλ° λΆλ΄μ μ§ νμκ° μλ€. 보ν΅μ ν¨μλ₯Ό Option
μ λν΄ μμ©νλ ν¨μλ‘ μΉκΈμν¬ μ μκΈ° λλ¬Έμ΄λ€.
def lift[A, B](f: A => B): Option[A] => Option[B] = _ map f
μ΄λ¬ν lift
κ° μμΌλ©΄ μ§κΈκΉμ§ λμ¨ κ·Έ μ΄λ€ ν¨μλΌλ ν Option
κ°μ λ¬Έλ§₯ μμμ μμ©νλλ‘ λ³νν μ μλ€.
val abs0: Option[Double] => Option[Double] = lift(math.abs)
μ μμμ 보λ―μ΄, μ νμ κ°μ μμ©νλ math.abs
ν¨μλ₯Ό μ§μ μμ±ν νμκ° μλ€. κ·Έλ₯ κ·Έ ν¨μλ₯Ό Option
λ¬Έλ§₯μΌλ‘ μΉκ²©μν€λ©΄ λλ€. μ΄λ¬ν μΉκΈμ λͺ¨λ ν¨μμ κ°λ₯νλ€.
π Either μλ£ νμβ
Option
μ΄ μμΈμ μΈ μ‘°κ±΄μ΄ λ°μνμ λ 무μμ΄ μλͺ»λμλμ§μ λν μ 보λ₯Ό μ 곡νμ§ λͺ»νλ€λ λ¨μ μ΄ μλ€. μ€ν¨ μ μ΄ νμμ κ·Έλ₯ μ ν¨ν κ°μ΄ μμμ λ»νλ None
μ λλ €μ€ λΏμ΄λ€. κ·Έλ¬λ κ·Έ μΈμ κ²μ΄ νμν λλ μλ€.
μ€ν¨μ κ΄ν΄ μκ³ μΆμ μ λ³΄κ° μ΄λ€ κ²μ΄λ κ·Έκ²μ λΆνΈννλ μλ£ νμμ λ§λλ κ²μ λ¬Όλ‘ κ°λ₯νλ€. κ·Έλ₯ μ€ν¨κ° λ°μνμμ μλ©΄ μΆ©λΆν λμλ Option
μ μ¬μ©νλ©΄ λλ€. κ·Έ μΈμ κ²½μ°μλ μ’ λ λ§μ μ λ³΄κ° νμνλ€.
sealed trait Either[+E, +A]
case class Left[+E](value: E) extends Either[E, Nothing]
case class Right[+A](value: A) extends Either[Nothing, A]
Option
μ²λΌ Either
λ case
κ° λ κ°λΏμ΄λ€. Option
κ³Όμ λ³Έμ§μ μΈ μ°¨μ΄λ, λ κ²½μ° λͺ¨λ κ°μ κ°μ§λ€λ κ²μ΄λ€. μμ£Ό κ°κ΄μ μΌλ‘ λ§νμλ©΄, Either
μλ£ νμμ λ μ€ νλμΌ μ μλ κ°λ€μ λννλ€. μ΄ νμμ λ νμμ λΆλ¦¬ν©μ§ν©(μλ‘ μ ν©μ§ν©)μ΄λΌ ν μ μλ€. μ΄ νμμ μ±κ³΅ λλ μ€ν¨λ₯Ό λνλ΄λ λ° μ¬μ©ν λμλ, Right
μμ±μλ₯Ό μ±κ³΅μ λνλ΄λ λ° μ¬μ©νκ³ (μ€λ₯Έμͺ½μ΄ μ³μ μͺ½) Left
λ μ€ν¨μ μ¬μ©νλ€. μΌμͺ½ νμ 맀κ°λ³μμ μ΄λ¦μΌλ‘λ error(μ€λ₯)λ₯Ό μλ―Ένλ E
λ₯Ό μ¬μ©νλ€.
κ·ΈλΌ mean
μμ λ₯Ό λ€μ 보μ. μ΄λ²μλ μ€ν¨μ κ²½μ°μ String
μ λλ €μ€λ€.
def mean(xs: IndexedSeq[Double]): Either[String, Double] =
if (xs.isEmpty)
Left("mean of empty list!")
else
Right(xs.sum / xs.length)
μ€λ₯μ λν μΆκ° μ 보, μ΄λ₯Όν
λ©΄ μμ€ μ½λμμ μ€λ₯κ° λ°μν μμΉλ₯Ό μ μ μλ μ€ν μΆμ μ λ³΄κ° μμΌλ©΄ νΈλ¦¬ν κ²½μ°κ° μ’
μ’
μλ€. κ·Έλ° κ²½μ° Either
μ Left
μͺ½μμ κ·Έλ₯ μμΈλ₯Ό λλ €μ£Όλ©΄ λλ€.
def safeDiv(x: Int, y: Int): Either[Exception, Int] =
try Right(x / y)
catch { case e: Exception => Left(e) }
Option
μμ νλ―μ΄, λμ Έμ§ μμΈλ₯Ό κ°μΌλ‘ λ³ννλ€λ μ΄λ¬ν 곡ν΅μ ν¨ν΄μ μΆμΆν ν¨μ Try
λ₯Ό μμ±ν΄ 보μ.
def Try[A](a: => A): Either[Exception, A] =
try Right(a)
catch { case e: Exception => Left(e) }
μ΄λ¬ν μ μλ€μ΄ μμΌλ©΄ Either
λ₯Ό for-ν¨μΆ
μ μ¬μ©ν μ μμμ μ£Όλ¬νμ.
def parseInsuranceRateQuote(
age: String,
numberOfSpeedTickets: String
): Either[Exception. Double] =
for {
a <- Try { age.toInt }
tickets <- Try { numberOfSpeedingTickets.toInt }
} yield insuranceRateQuote(a, tickets)
μ΄μ λ μ€ν¨ μ κ·Έλ₯ None
μ΄ μλλΌ λ°μν μ€μ μμΈμ λν μ 보λ₯Ό μ»κ² λμλ€.
λ§μ§λ§ μλ‘, λ€μμ map2
λ₯Ό μ μ©ν μλ€. ν¨μ mkPerson
μ μ£Όμ΄μ§ μ΄λ¦κ³Ό λμ΄μ μ ν¨μ±μ μ κ²ν ν μ ν¨ν Person
μ μμ±νλ€.
// Eitherλ₯Ό μλ£ μ ν¨μ± μ κ²μ νμ©
case class Person(name: Name, age: Age)
sealed class Name(val value: String)
sealed class Age(val value: Int)
def mkName(name: String): Either[String, Name] =
if (name == "" || name == null) Left("Name is empty.")
else Right(new Name(name))
def mkAge(age: Int): Either[String, Age] =
if (age < 0) Left("Age is out of range.")
else Right(new Age(age))
def mkPerson(name: String, age: Int): Either[String, Person] =
mkName(name).map2(mkAge(age))(Person(_, _))