Skip to content
LazySoul edited this page Sep 24, 2016 · 3 revisions

컨테이너로서의 Option

Option은 이진 컨테이너입니다. 즉 어떤 원소를 포함하고 있거나. 그렇지 않거나 둘 중 하나입니다. Option도 우리에게 필요한 4가지 메서드를 모두 제공합니다.

sealed abstract class Option[A+] { self =>
  ...
  def isEmpty: Boolean // Some 인이나  None이 궇구혆구현한구현한다.
  
  final def foreach[U](f: A=> U): Unit = 
    if (!isEmpty) f(this.get)
    
  final def map[B](f: A => B): Option[B] = 
    if (isEmtpy) None else Some(f(this.get))
    
  final def flatmap[B](f: A => Option[B]): Option[B] = 
    if (isEmtpy) None else Some(f(this.get))
    
  final def filter(p: A=>Boolean) : Option[B] =
    if (isEmpty || p(this.get)) this else None
    
  final def withFilter(p: A => Boolean): WithFilter = new WithFilter(p)
  
  class WithFilter(p: A => Boolean) {
    def map[B](f: A => B): Option[B] = self filter p map f
    def flatMap[B](f: A => Ootion[B]): Option[B] = self filter p flatmap f 
    def foreach[U](f: A => U): Unit = self filter p foreach f
    def withFilter(q: A => Boolean): WithFilter = 
      new WithFilter(x => p(x) && q(x))
  }
}

final 키워드는 서브클래스가 이 구현을 오버라이딩 하지 못하도록 막는다. Option은 비어있거나 (None) 그렇지 않거나 (Some), 둘 중 하나다.

val results: Seq[Option[Int]] = Vector(Some(10), None, Some(20))

val result2 = for {
  Some(i) <- results
} yield(2 * i)
//반환 Seq[Int] = Vector(20, 40)

Some(i) <- list 패턴은 results 의 원소와 매치되면서, None값을 제거하고 Some 값 안의 정수를 뽑아낸다.

위 코드처럼 변환 규칙을 적용해서 각각의 pat <- expr 식을 withFilter식으로 바꾼것이다.

val results2b = for {
  Some(i) <- results withFilter {
    case Some(i) => true
    case None => false
} yield (2 * 1)
//반환 : results2b: List[Int] = List(20, 40)

바깥쪽의 for { x <- y } yield (z) 식을 map 호출로 바꾼다.

val results2c = results withFilter {
  case Some(i) => true
  case None => false
} map {
  case Some(i) => (2 * i)
}
//반환 : results2c : List[Int] = List(20,40)

실제로는 map 식에서 컴파일러가 경고를 발생시킨다.

<<console>:9: warring: match may not be exhaustive.
It would fail on the following input : None
       } map {
             ^

보통은 map에 case None => ...절이 없는 부분함수를 넘기는 것은 위험하다. None이 들어오면 MatchError예외가 던져질 것이다. 하지만 withFilter 호출이 이미 None 원소를 제거했기 때문에 그 예외는 발생할 수 없다.

독립적인 작업을 여럿 실행하면서 결과가 없는 경우를 무시하고 정상적인 결과를 취합하는 대신, 의존 관계가 있는 여러 단계의 작업을 순차적으로 실행하면서, None이 발생하자마자 전체 과정을 중단하고 싶은 경우를 생각해보자.

def positive(i: Int): Option[Int] = 
  if (i > 0) Some(i) else None

for {
  i1 <- positive(5)
  i2 <- positive(10 * i1)
  i3 <- positive(25 * i2)
  i4 <- positive(2  * i3)
} yield (i1 + i2 + i3 + i4)
// Returns: Option[Int] = Some(3805)

for {
  i1 <- positive(5)
  i2 <- positive(-1 * i1)              // <1>   EPIC FAIL!
  i3 <- positive(25 * i2)              // <2>
  i4 <- positive(-2 * i3)              // EPIC FAIL!
} yield (i1 + i2 + i3 + i4)
// Returns: Option[Int] = None
Clone this wiki locally