제안된 언어 기능의 분류

제안된 언어 기능의 분류 (A Classification of Proposed Language Features)

이 문서는 스칼라 3에 제안된 구성들의 개요를 제공해서, 무엇을 언제 포함할지에 대한 논의를 돕는 걸 목표로 해요. 기능을 여덟 그룹으로 분류해요: (1) 필수 기반, (2) 단순화, (3) 제한, (4) 제거된 기능, (5) 변경된 기능, (6) 새 기능, (7) 기존 매크로를 대체하는 데 목적을 둔 메타프로그래밍 중심 기능, (8) 타입 체킹과 추론의 변경.

출처: Scala 3 Reference

본문

각 그룹에는 상태(즉 스칼라 3의 일부가 되는 상대적 중요성, 그리고 언제 결정할지의 상대적 시급성)와 그 구성들의 마이그레이션 비용을 분류하는 절이 있어요.

현재 문서는 2019년 4월 기준 상황을 반영해요. 앞으로 그 상태의 변경을 반영하도록 업데이트될 거예요.

필수 기반 (Essential Foundations)

이 새 구성들은 DOT, 고차 종류(higher-kinded types), implicit 해석을 위한 SI 계산의 핵심 기능을 직접 모델링해요.

  • 교차 타입(Intersection types), compound types를 대체
  • 합집합 타입(Union types)
  • 타입 람다(Type lambdas), 구조적 타입과 타입 투영을 쓰는 인코딩을 대체
  • given 파라미터에 대한 추상화를 제공하는 컨텍스트 함수(Context functions)

상태: 필수

이들은 스칼라 3의 필수 핵심 기능이에요. 이들 없이는 스칼라 3는 완전히 다른 기반을 가진 완전히 다른 언어가 될 거예요.

마이그레이션 비용: 없음에서 낮음

이들은 추가이므로, 일반적으로 옛 코드에는 마이그레이션 비용이 없어요. 예외는 교차 타입인데, 이는 약간 정리된 시맨틱으로 compound types를 대체해요. 하지만 이 변경에 영향받는 프로그램은 거의 없을 거예요.

단순화 (Simplifications)

이 구성들은 기존 구성들을 대체해서 언어를 더 안전하고 단순하게 쓰도록 만들고, 코드 스타일의 통일성을 촉진하는 걸 목표로 해요.

  • 트레잇 파라미터는 조기 초기화자(early initializers)를 더 일반적으로 유용한 구성으로 대체해요.
  • given instances는 implicit objects와 defs를 대체하며, 메커니즘보다 의도를 강조해요.
  • using 절은 implicit 파라미터를 대체하며, 그 모호성을 피해요.
  • 확장 메서드(extension methods)는 implicit classes를 더 명확하고 단순한 메커니즘으로 대체해요.
  • 불투명 타입 별칭(opaque type aliases)은 대부분의 값 클래스(value class) 사용을 대체하면서 boxing이 없음을 보장해요.
  • 최상위 정의(top-level definitions)는 패키지 객체를 대체하며, 문법적 보일러플레이트를 없애요.
  • export 절은 집합(aggregation)을 표현하는 간단하고 일반적인 방법을 제공하며, 클래스에서 상속하는 패키지 객체의 이전 파사드 패턴을 대체할 수 있어요.
  • 가변 인자 스플라이스(vararg splices)는 이제 @ _* 대신 * 형태를 사용하며, vararg 표현식을 반영해요.
  • 생성자 애플리케이션(creator applications)은 new 표현식 대신 간단한 함수 호출 문법을 쓰게 해줘요. new 표현식은 생성자 애플리케이션을 쓸 수 없는 경우의 폴백으로 남아 있어요.

조기 초기화자와 옛 방식 vararg 스플라이스를 제외하면, 대체된 모든 구성은 스칼라 3.0에서 계속 사용 가능해요. 나중에 폐기하고 단계적으로 없앨 계획이에요.

값 클래스(불투명 타입 별칭으로 대체됨)는 특별한 경우예요. 현재 값 클래스에 대한 폐기 계획은 없어요. JVM이 프로젝트 Valhalla에서 계획한 대로 네이티브로 지원하게 된다면 더 일반적인 형태로 되돌릴 수도 있기 때문이에요.

상태: 이중 모드: 지금 아니면 영영 / 지연 가능

이들은 필수적인 단순화예요. 채택하기로 결정했다면 3.0에서 해야 해요. 그렇지 않으면 스칼라 3 문서가 미래에 더 단순한 것으로 대체·대치될 옛 기능을 설명해야 하는 어색한 상황에 부딪히게 돼요.

다른 한편으로, 이 목록에서 새 기능에 대해서만 지금 결정하면 돼요. 대체된 기능을 제거할지 결정은 지연할 수 있어요. 물론 대체될 기능을 제거하기로 결정하지 않고 새 기능을 채택하면 언어가 더 커지게 돼요.

마이그레이션 비용: 보통

다음 몇 버전 동안 옛 기능은 계속 사용 가능할 테니, 폐기와 재작성 기법으로 모든 마이그레이션 노력을 낮고 점진적으로 만들 수 있어요.

제한 (Restrictions)

이 구성들은 언어를 더 안전하게 만들기 위해 제한돼요.

  • Implicit Conversions: implicit 변환을 정의하는 방식이 여러 가지가 아니라 단 하나만 있어요. 그리고 잠재적으로 놀랄만한 implicit 변환은 언어 임포트를 요구해요.
  • Given Imports: implicit는 이제 특별한 형태의 import를 요구해서, import를 분명히 보이게 해요.
  • Type Projection: 타입 투영 C#A의 접두사 C로는 클래스만 쓸 수 있어요. 추상 타입에 대한 타입 투영은 타당하지 않으므로 더 이상 지원하지 않아요.
  • Multiversal equality는 ==!=로 의미 없는 비교를 배제하는 "opt-in" 방식을 구현해요.
  • infix는 메서드 적용 문법을 코드 베이스 전반에 걸쳐 통일시켜요.

제한 없는 implicit 변환은 스칼라 3.0에서 계속 사용 가능하지만, 나중에 폐기되고 제거될 거예요. 위 목록의 다른 구성들의 제한 없는 버전은 -source 3.0-migration 아래에서만 사용 가능해요.

상태: 지금 아니면 영영

이들은 필수적인 제한이에요. 채택하기로 결정했다면 3.0에서 해야 해요. 그렇지 않으면 스칼라 3 문서가 미래에 제한될 기능을 설명해야 하는 어색한 상황에 부딪히게 돼요.

마이그레이션 비용: 낮음에서 높음

  • 낮음: multiversal equality는 의미 없는 코드를 배제하므로, 채택으로 요구되는 재작성은 버그 수정으로 분류되어야 해요.
  • 보통: implicit에 대한 제한은 직관적인 재작성으로 수용할 수 있어요.
  • 높음: 제한 없는 타입 투영은 일반적으로 타당하지 않으므로 항상 직접 재작성할 수는 없어요.

제거된 구성 (Dropped Constructs)

이 구성들은 대체하는 새 구성 없이 제거하도록 제안됐어요. 제거 동기는 언어와 그 구현을 단순화하기 위해서예요.

  • DelayedInit,
  • Existential types,
  • Procedure syntax,
  • Class shadowing,
  • XML literals,
  • Symbol literals,
  • Auto application,
  • Weak conformance,
  • Compound types,
  • Auto tupling (구현됐지만 병합되지 않음).

이 구성들이 제거되는 시기는 다양해요. 현재 상태는 다음과 같아요.

  • 전혀 구현되지 않음: DelayedInit, existential types, weak conformance.
  • -source 3.0-migration 아래에서 지원됨: procedure syntax, class shadowing, symbol literals, auto application, 제한된 형태의 auto tupling.
  • 3.0에서 지원되고 나중에 폐기·단계 제거됨: XML literals, compound types.

상태: 혼합

현재 구현되지 않은 기능들은 상당한 구현 노력을 요구하며, 대부분의 경우 컴파일러를 더 버그투성이이고 취약하며 이해하기 어렵게 만들 거예요. 제거하기로 결정하지 않으면, 아마도 스칼라 3.0 릴리스에서 "아직 구현되지 않음"으로 나타날 거예요.

현재 구현된 기능들은 무기한 남아 있을 수 있어요. 업데이트된 문서는 그것들이 결국 사라질 거라는 기대 속에 그냥 무시할 수 있어요. 그러니 그 제거에 대한 결정은 지연할 수 있어요.

마이그레이션 비용: 보통에서 높음

제거된 기능은 프로그램에서 그 사용을 피하기 위해 재작성을 요구해요. 이 재작성은 때로 자동(예: procedure syntax, symbol literals, auto application)이고 때로 수동(예: class shadowing, auto tupling)일 수 있어요. 때로는 재작성이 사용 지점뿐 아니라 정의 지점에도 영향을 주는 비지역적(non-local)이어야 할 수 있어요(예: DelayedInit의 경우, 해결책을 찾지 않으면).

변경 (Changes)

이 구성들은 더 규칙적이고 유용하게 만들기 위해 변경되었어요.

  • Structural Types: 이제 플러그 가능한 구현을 허용해서 유용성이 크게 높아졌어요. 일부 사용 패턴은 기존 상태와 비교해 제한돼요.
  • Name-based pattern matching: 기존의 문서화되지 않은 스칼라 2 구현이 약간 단순화된 형태로 규정되었어요.
  • Eta expansion: 이제 기대 타입이 없을 때도 보편적으로 수행돼요. 따라서 후위 _ 연산자는 중복이 됐고, 스칼라 3.0 이후에 폐기·제거될 거예요.
  • Implicit Resolution: implicit 해석 규칙이 더 유용하고 덜 놀랍게 정리되었어요. implicit 범위는 더 이상 패키지 접두사를 포함하지 않도록 제한돼요.

옛 방식 implicit 해석의 대부분 측면은 -source 3.0-migration 아래에서 계속 사용 가능해요. 이 목록의 다른 변경들은 무조건 적용돼요.

상태: 강력히 권장

이 기능들은 새 형태로 스칼라 3.0의 컴파일러에 구현되었어요. 기존 상태와 비교해 단순성과 기능에서 분명한 개선을 제공해요. 되돌아가려면 기능의 순손실에 상당한 구현 노력이 필요해요.

마이그레이션 비용: 낮음에서 높음

변경이 필요한 프로그램은 몇 개뿐일 거예요. 하지만 일부 필요한 변경은 비지역적일 수 있어요(implicit 범위 제한의 경우처럼).

새 구성 (New Constructs)

이것들은 언어를 더 강력하거나 쓰기 즐겁게 만드는 추가예요.

  • Enums는 열거형과 대수적 데이터 타입(ADT)을 위한 간결한 문법을 제공해요.
  • Parameter untupling은 튜플 파라미터 구조 분해에 case를 쓰지 않아도 되게 해줘요.
  • Dependent function types는 의존적 메서드를 의존적 함수 값과 타입으로 일반화해요.
  • Polymorphic function types는 다형 메서드를 의존적 함수 값과 타입으로 일반화해요. 현재 상태: 제안과 프로토타입 구현이 있지만, 구현이 확정·병합되지는 않았어요.
  • Kind polymorphism은 타입과 타입 생성자에 동등하게 작동하는 연산자 정의를 허용해요.

상태: 혼합

Enums는 근본적인 사용 패턴의 필수적 단순화를 제공하므로, 스칼라 3.0에 채택되어야 해요. 자동 파라미터 튜플링은 몇몇 어색함을 제거하는 아주 작은 변경이므로 지금 채택해도 좋아요. 다른 기능들은 더 전문화된 기능으로서 나중 버전에서 도입될 수 있어요. 다른 한편으로, 다형 함수 타입을 제외하면 모두 완전히 구현되어 있으므로, 스칼라 3.0 스펙에 포함되지 않아도 언어 플래그 아래에서 여전히 사용 가능하게 만들 수 있어요.

마이그레이션 비용: 없음

새 기능이므로, 기존 코드는 변경 없이 마이그레이션돼요. 확실히, 명확성과 간결함을 높이기 위해 새 기능을 활용하도록 코드를 재작성하는 게 매력적일 때도 있어요.

메타프로그래밍 (Metaprogramming)

다음 구성들은 함께 스칼라의 메타프로그래밍을 새 기반 위에 놓는 것을 목표로 해요. 지금까지 메타프로그래밍은 매크로와, 몇몇 핵심 매크로에 기반한 Shapeless 같은 라이브러리의 조합으로 이루어졌어요. 현재 스칼라 2 매크로 메커니즘은 현재 스칼라 2 컴파일러 위의 얇은 겉장식(veneer)이라서 취약하고, 많은 경우 스칼라 3로 포팅하는 게 불가능해요.

매크로는 스칼라 2 언어 스펙에 결코 포함되지 않았고 지금까지 -experimental 플래그 아래에서만 사용 가능했다는 점을 주목할 만해요. 이는 매크로의 광범위한 사용을 막지 못했어요.

매크로 사용의 대부분을 포팅할 수 있게 하기 위해, 우리는 아래 나열된 고급 언어 구성들을 실험하고 있어요. 이 설계들은 스칼라 3.0의 나머지 제안 언어 구성들보다 더 잠정적이에요. 최종 릴리스까지 몇 가지 변경이 있을 수 있어요. 메타프로그래밍에 필요한 기능 집합을 안정화하는 것이 우리의 최우선 순위예요.

  • Match types는 타입에 대한 계산을 허용해요.
  • Inline은 그 자체로 몇몇 간단한 매크로의 직관적인 구현을 제공하고, 동시에 복잡한 매크로 구현의 필수 구성 요소예요.
  • Quotes와 splices는 통합된 추상화 집합으로 매크로와 스테이징을 표현하는 원칙적인 방법을 제공해요.
  • Type class derivation은 Shapeless의 Gen 매크로와 다른 기반 라이브러리를 언어 내 구현으로 제공해요. 새 구현은 매크로보다 더 견고하고 효율적이며 쓰기 쉬워요.
  • Implicit by-name parameters는 Shapeless의 Lazy 매크로를 더 견고한 언어 내 구현으로 제공해요.

상태: 아직 확정되지 않음

현재 매크로의 실용적 대체가 필요하다는 건 알아요. 위 기능들은 그 점에서 매우 유망하지만, 최종 판단을 내리려면 더 완전한 구현과 더 많은 사용 사례가 필요해요.

마이그레이션 비용: 매우 높음

기존 매크로 라이브러리는 처음부터 다시 작성해야 할 거예요. 많은 경우 재작성된 라이브러리는 옛 것보다 단순하고 견고해지겠지만, 그렇다고 재작성 비용이 없어지진 않아요. 매크로 라이브러리 사용자가 어느 정도 영향받을지는 현재 불분명해요. 핵심 매크로를 완전히 재구현할 수 있도록 충분한 기능을 제공하는 게 목표지만, 스칼라 2에 대한 다양한 매크로 확장의 방대한 기능 집합을 고려하면 실현 가능한 범위 제한을 정하는 게 어려워요.

타입 체킹과 추론의 변경 (Changes to Type Checking and Inference)

스칼라 3 컴파일러는 일반적인 서브타입 제약 해결사(subtype constraint solver)에 의존하는 새 타입 추론 알고리즘을 사용해요. 새 알고리즘은 종종 옛 것보다 잘 동작하지만, 두 알고리즘의 결과가 달라져서 스칼라 2 컴파일러가 받아들이는 프로그램에 대해 스칼라 3가 오류를 진단하는 상황이 불가피하게 있어요.

상태: 필수

새 타입 체킹과 추론 알고리즘은 새 컴파일러의 필수 핵심이에요. 스칼라 3의 전체 구현을 버리지 않고는 되돌릴 수 없어요.

마이그레이션 비용: 높음

일부 기존 프로그램은 깨질 거예요. 그리고 타입 추론의 복잡한 특성상, 어떤 변경이 깨짐을 유발했는지, 어떻게 고칠지가 항상 분명하지 않을 거예요.

우리 경험상, 매크로와 타입·implicit 인자 추론의 변경이 함께 스칼라 3로 기존 코드를 포팅할 때 만나는 문제의 대부분을 유발해요. 후자 문제의 원천은 추론된 타입과 implicit 인자를 모두 스칼라 2 소스 파일에 추가하는 도구로 체계적으로 다룰 수 있어요. 아마도 그런 도구는 스칼라 2 컴파일러 플러그인으로 구현될 거예요. 결과 코드는 스칼라 3에서 컴파일될 가능성이 크게 높아지겠지만, 종종 읽기 어려울 정도로 방대해질 거예요. 재작성 도구의 두 번째 부분은 첫 부분이 합성한 타입·implicit 어노테이션을, 스칼라 3에서 컴파일되는 한 선택적으로 반복적으로 제거해야 해요. 이 두 번째 부분은 스칼라 3 컴파일러 scalac를 프로그램 방식으로 호출하는 프로그램으로 구현할 수 있어요.

몇몇 사람이 한동안 그런 도구를 제안해 왔어요. 이제 실제로 구현할 의지와 자원을 찾을 때라고 생각해요.