Embrace Swift type inference

WWDC 2020

스위프트는 코드의 안전성을 훼손하지 않고 간결한 코드를 작성하기 위해 타입 추론(Type inference)을 광범위하게 사용한다.

이 영상을 통해 우리는 다음의 것들을 살펴볼 것이다.

  1. 타입 추론을 활용하는 법
  2. 컴파일러에서 타입 추론이 어떻게 동작하는지
  3. 타입 추론에 의해 발생할 수 있는 에러의 원인과 이를 해결하는 방법

What is type inference?

먼저 타입 추론이 무엇인지 간단하게 알아보자.

타입 추론은 프로퍼티에 타입을 명시적으로 선언하지 않아도 컴파일러가 문맥에 따라 타입을 추론하는 것을 말한다.

img

위의 코드에서 String을 명시적으로 선언하지 않아도 컴파일러는 이를 String으로 추론한다. 좀 더 복잡한 예제 코드로 타입 추론을 활용하고, 컴파일러에서 타입 추론이 어떻게 동작하는지를 살펴보자.

img

위의 코드에서 FilteredList는 주어진 데이터를 리스트 형태로 보여주고 필터링 기능을 제공하는 재사용 가능한 뷰다. 이 FilteredList는 재사용 가능해야 하므로 기본적으로 생성자 인자들은 제네릭 해야 한다. 위의 코드에서 FilteredList를 사용하면서 따로 타입을 명시해 주지 않고 있다. 이는 컴파일러가 타입 추론을 하기 때문에 가능한 일인데, 이를 좀 더 알아보기 위해 FilteredList가 어떻게 정의되어 있는지 코드로 살펴보도록 하자

img

Element, FilterKey 그리고 RowContentFileteredList가 생성될 때 실제 타입, 즉 Concrete 타입으로 대체된다. 이제 선언부와 호출부를 나란히 두고 비교해보자.

img

제네릭 타입으로 인해 복잡한 선언부와 비교했을 때 호출부는 훨씬 깔끔한 코드임을 확인할 수 있다. 이는 컴파일러가 주어진 값들로 타입 추론을 하기 때문에 가능한 일인데, 타입 추론이 아닌 명시적으로 타입을 명시한다면 아래와 보다 복잡한 코드를 작성해야 한다.

글

그렇다면 컴파일러는 어떻게 타입 추론을 하는 것일까? 타입 추론은 일종의 퍼즐이라고 할 수 있다. 우린 퍼즐을 하면서 하나의 조각이 맞춰질 때 다음 조각을 자연스럽게 유추할 수 있다. 하나의 퍼즐이 맞춰질 때마다 다음 조각에 대한 단서를 우린 유추할 수 있다. 컴파일러는 이렇게 퍼즐을 풀 듯이 우리의 코드에서 단서를 찾아 퍼즐을 하나씩 맞춰가며 타입을 추론한다.

위의 코드를 사용해서 컴파일러가 어떻게 퍼즐을 맞춰가는지 살펴보자.

img

먼저 첫 번째로 인자로 넘기는 smoothies라는 단서를 통해 Element의 타입을 추론할 수 있다. smoothies[Smoothie] 타입으로 ElementSmoothie 타입으로 대체된다.

img

우린 Element라는 퍼즐 조각을 맞췄기 때문에 이를 통해 또 다른 단서를 얻을 수 있다. 바로 FilterKey다. \.title\Smoothe.title로 대체되고 Smoothietitle 프로퍼티는 String이란 것을 알 수 있기 때문에 FilterKeyString으로 대체된다.

img

img

RowContent 역시 ViewBuilder 클로저 안에서 SmoothieRowView가 반환되기 때문에 SmoothieRowView로 대체될 수 있다.

img

img

이런 식으로 컴파일러는 이전 단계의 단서를 통해 하나씩 타입을 추론해 나간다. 하지만 이렇게 얻은 이전 단계의 퍼즐 조각(단서)이 맞지 않는다면 소스 코드에 에러가 발생했다는 것을 의미한다. 즉 맞지 않은 타입이 들어갔기 때문에 컴파일러는 더 이상 타입 추론을 진행할 수 없다.

Smoothie.title이 아닌 Bool 타입의 Smoothie.isPopular로 바꿔보자

img

그렇다면 컴파일러는 Bool 타입을 FilterKey의 조각으로 사용할 것이다. 하지만 Bool 타입은 hasSubString(_:) 메서드가 없기 때문에 이후의 타입 추론을 진행할 수 없고, 에러를 뱉는다.

img

이렇게 스위프트 컴파일러는 추후에 에러 메시지를 출력할 때 사용하기 위해 에러 추적 기능을 타입 추론에 통합시켰다. 컴파일러는 타입 추론을 진행하면서 직면한 에러를 기록한다. 그리고 컴파일러는 에러를 고치고 타입 추론을 계속 진행하기 위해 휴리스틱을 사용한다.

그리고 타입 추론이 끝나면 컴파일러는 타입 추론을 진행하면서 수집한 에러를 actionable한 에러 메시지(자동으로 코드를 수정할 수 있는)나 에러를 발생시킨 실제 타입에 대한 메시지와 함께 개발자에게 알린다.

img

이렇게 통합된 에러 추적 시스템은 Xcode11.4의 스위프트 5.2에선 많은 오류 메시지에 도입되었고, Xcode12의 스위프트 5.3에선 모든 에러 메시지에 적용되었다. Embrace Swift type inference