About Author

daewon
blog: http://daewon.github.com
twitter: @blueiur
github: daewon

about.me/daewon

사이냅소프트 근무

웹오피스 개발

About this Article

Date Released:
Tuesday, July 23 2013 2:00 AM

Why throw has a type: Nothing in Scala

Scala에서 throw 는 왜 타입이 필요할까? 아래 예문을 살펴보자.

def error(message: String): Nothing = throw new RuntimeException(message)
def divide(x: Int, y: Int): Int = {
  if (y != 0) x / y
  else error("can't divide by zero")
}

몇가지 특이한 점을 발견할 수 있다.

  1. divid 함수의 반환 타입은 Int이다. 그런데 divid 함수의 본문은 if/else 뿐이며 ifelse의 반환 타입이 다르다.
  2. error 함수의 반환 타입은 Nothing이다. 그런데 error함수의 본문은 throw 뿐이다. throw가 되면 실행이 중지될 텐데 타입이 왜 필요할까?

이제 궁금한 점을 하나씩 풀어보자.

Nothing 타입은 무엇이며 왜 필요한가?

Scala는 다른 Functional language들과 마찬가지로 모든 문장(statements)은 식(expressions)이다. 즉 모든 문장(식)이 평가되면 반드시 어떠한 결과 값을 반환해야 한다. 때문에 throw 역시 반환값을 가져야 한다.

throw는 기본적으로 control flow에 관여한다. throw 문이 평가되면 exception이 발생되기 때문에 반환 타입은 아무런 의미가 없는 것으로 보인다. 그렇다면 throw는 어떤 타입을 가져야 하며 어떤 의미가 있을까?

throw의 반환 타입을 repl로 확인해 보자

scala> :type throw new Exception
=> Nothing

반환 타입은Nothing이다. 어떤 의미가 있을까? Scala's type hierachy

Scala's type hierarchy

위 그림에서 알 수 있듯이 Nothing은 모든 타입에 하위타입이다. 즉 어떤 타입이 필요하든 그 자리에는 Nothing이 위치할 수 있다.

위에서 Scala에서 모든 문장은 식이며 언제나 반환값을 가져야 한다고 했다. 그렇다면 if는 어떨까? if 역시 마찬가지로 식이며 반환값을 가져야 한다.

확인을 위해 아래 구문을 살펴보자

scala> :type if (true) 10 else "oops"
=> Any

위 식의 반환 타입은 scala.Int(10)과 java.lang.String("oops")의 공통 최 상위 타입 Any다.

식의 평가 값을 할당할 때 type이 결정 되여야 하기 때문에 공통 조상 타입이 할당된다.

이제 다시 아래 예문을 살펴보자

def error(message: String): Nothing = throw new RuntimeException(message)
def divide(x: Int, y: Int): Int = {
  if (y != 0) x / y
  else error("can't divide by zero")
}

스칼라는 강타입(static-type) 언어다. 때문에 컴파일 타임에 모든 타입이 결정되야 한다.

위 예문이 컴파일 되려면 if/else 절의 타입이 정해져야 한다. 그렇기 때문에 throw문의 반환값이 필요하며 적절한 타입이 위치해야 한다.

위 예문에서 divide함수의 반환값이 Int이며 if절이 Int를 반환하기 때문에 else절 역시 Int 혹은 그에 해당하는 하위 타입이 위치해야 한다.

이런 이유로 모든 타입에 하위 타입인 Nothing이 필요하게 된다.

요약하자면

  1. Scala에 모든 문은 식이다. 즉 반환값이 있어야 한다.
  2. 컴파일 타임에 타입이 결정되야 한다.
  3. if/else 역시 식이며 반환 타입이 필요하다.
  4. throw 에 반환 타입은 실행상 아무 의미가 없지만 컴파일 시 타입 결정에 관여한다.