호환성과 폐기(deprecation)
호환성과 폐기(deprecation)
Elixir는 vMAJOR.MINOR.PATCH 스키마로 버전을 관리해요.
Elixir는 현재 메이저 버전 v1입니다. 새로운 하위 호환 마이너 릴리즈는 6개월마다 나옵니다. 패치 릴리즈는 정해진 일정이 없고, 버그 수정이나 보안 패치가 있을 때마다 이뤄져요.
Elixir는 버그 수정을 최신 마이너 브랜치에만 적용합니다. 보안 패치는 최근 5개 마이너 브랜치에 제공됩니다:
| Elixir 버전 | 지원 |
|---|---|
| 1.20 | 버그 수정 및 보안 패치 |
| 1.19 | 보안 패치만 |
| 1.18 | 보안 패치만 |
| 1.17 | 보안 패치만 |
| 1.16 | 보안 패치만 |
새 릴리즈는 읽기 전용 announcements 메일링 리스트에서 공지됩니다. 모든 보안 릴리즈는 [security] 태그가 붙습니다.
현재 메이저 v2 릴리즈 계획은 없어요.
마이너 Elixir 버전 사이
Elixir 마이너·패치 릴리즈는 하위 호환됩니다. 특정 버전에서 잘 정의된 동작과 문서화된 API는 이후 버전에서도 계속 동작해요.
압도적 다수의 프로그램이 시간이 지나도 호환될 것으로 기대하지만, 어떤 미래 변경도 어떤 프로그램도 깨뜨리지 않는다는 것을 보장하는 것은 불가능합니다. 드문 상황에서는 기존 코드를 깨는 변경을 도입할 수 있어요:
- 보안: 구현에서 보안 문제가 발생해, 그 해결을 위해 하위 호환되지 않는 변경이 필요할 수 있습니다. 우리는 이런 보안 문제를 처리할 권리를 보유합니다.
- 버그: API에 바람직하지 않은 동작이 있다면, 버그 동작에 의존하는 프로그램은 버그가 고쳐지면 깨질 수 있어요. 우리는 이런 버그를 고칠 권리를 보유합니다.
- 컴파일러 프론트엔드: 컴파일러를 개선해 모호한 모드에 대한 새 경고를 도입하고 더 상세한 오류 메시지를 제공할 수 있어요. 이는 (
--warnings-as-errors로 실행할 때) 컴파일 오류나, 특정 오류 메시지를 단정할 때 도구 실패로 이어질 수 있습니다(다만 그런 것은 피해야 해요). 우리는 이런 개선을 할 권리를 보유합니다. - 임포트: 자동 임포트되는
Kernel모듈에 새 함수가 추가될 수 있어요. 그것들은 여러분의 모듈에 정의된 로컬 함수와 충돌할 수 있습니다. 충돌은Kernel에서 임포트되길 원하지 않는 모든 함수의 목록과 함께import Kernel, except: [...]을 사용해 하위 호환 방식으로 해결할 수 있어요. 우리는 이런 추가를 할 권리를 보유합니다.
언어를 깨는 변경 없이 계속 진화시키기 위해, Elixir는 폐기(deprecation)에 의존해 특정 관행을 격하하고 새로운 관행을 홍보합니다. 폐기 정책은 "Deprecations" 섹션에 요약되어 있어요.
위 호환성 보증의 유일한 예외는 실험적 기능입니다. 그것들은 명시적으로 그렇게 표시되며, 안정화되기 전까지는 어떤 호환성 보증도 제공하지 않아요.
Elixir와 Erlang/OTP 사이
Erlang/OTP 버전 관리는 Elixir의 버전 관리와 독립적이에요. Erlang은 매년 새 메이저 버전을 릴리즈합니다. 우리의 목표는 Elixir가 릴리즈될 시점에 최근 3개의 Erlang 메이저 버전을 지원하는 것입니다. 호환성 표는 아래와 같아요.
| Elixir 버전 | 지원되는 Erlang/OTP 버전 |
|---|---|
| 1.20 | 27 - 29 |
| 1.19 | 26 - 28 |
| 1.18 | 25 - 27 |
| 1.17 | 25 - 27 |
| 1.16 | 24 - 26 |
| 1.15 | 24 - 26 |
| 1.14 | 23 - 25 (그리고 v1.14.5부터 Erlang/OTP 26) |
| 1.13 | 22 - 24 (그리고 v1.13.4부터 Erlang/OTP 25) |
| 1.12 | 22 - 24 |
| 1.11 | 21 - 23 (그리고 v1.11.4부터 Erlang/OTP 24) |
| 1.10 | 21 - 22 (그리고 v1.10.3부터 Erlang/OTP 23) |
| 1.9 | 20 - 22 |
| 1.8 | 20 - 22 |
| 1.7 | 19 - 22 |
| 1.6 | 19 - 20 (그리고 v1.6.6부터 Erlang/OTP 21) |
| 1.5 | 18 - 20 |
| 1.4 | 18 - 19 (그리고 v1.4.5부터 Erlang/OTP 20) |
| 1.3 | 18 - 19 |
| 1.2 | 18 - 18 (그리고 v1.2.6부터 Erlang/OTP 19) |
| 1.1 | 17 - 18 |
| 1.0 | 17 - 17 (그리고 v1.0.5부터 Erlang/OTP 18) |
Elixir는 패치 릴리즈에서 새 Erlang/OTP 버전에 대한 호환성을 추가할 수 있어요. 예를 들어 v1.4.5에서 Erlang/OTP 20 지원 같은 경우가 그렇습니다. 그런 릴리즈는 편의를 위해 만들어지며, 필요하다면 Elixir가 오류 없이 실행되기 위한 최소 변경(예: 어떤 변경이 필요할 경우)만 포함하는 것이 일반적이에요. 오직 다음 마이너 릴리즈(여기서는 v1.5.0)만이 최신 Erlang/OTP 릴리즈가 제공하는 새 기능을 효과적으로 활용합니다.
본문
정책
Elixir의 폐기는 3단계로 진행됩니다:
- 기능이 **소프트 폐기(soft-deprecated)**됩니다. CHANGELOG와 문서에 기능이 deprecated임을 나열해야 하지만, 코드 실행 시 실제 경고는 방출되지 않아요. 기능을 소프트 폐기할 의무는 없습니다.
- 사용 시 경고를 방출함으로써 기능이 **실질적으로 폐기(hard-deprecated)**됩니다. 기능을 폐기하려면 제안된 대안이 최소 3개 마이너 버전 동안 존재해야 해요. 예를 들어
Enum.uniq/2는 Elixir v1.1에서Enum.uniq_by/2를 위해 소프트 폐기되었어요. 이는 Elixir v1.4 이상에서만 폐기 경고가 방출될 수 있음을 의미합니다. - 기능이 제거됩니다. 이는 오직 메이저 릴리즈에서만 일어날 수 있어요. 즉 Elixir v1.x에서 폐기된 기능은 Elixir v2.x에서만 제거됩니다.
폐기 표
첫 번째 열은 기능이 hard deprecated된 버전입니다. 두 번째 열은 폐기된 기능을 짧게 설명하고, 세 번째 열은 대안과 그 대안이 제공되기 시작한 버전을 설명해요.
| 버전 | 폐기된 기능 | 대체 (제공 시작 버전) |
|---|---|---|
| v1.20 | ^ 없는 패턴의 <<x::size(y)>> |
<<x::size(^y)>> (v1.15) |
| v1.20 | File.stream!(path, modes, lines_or_bytes) |
File.stream!(path, lines_or_bytes, modes) (v1.16) |
| v1.20 | Kernel.ParallelCompiler.async/1 |
Kernel.ParallelCompiler.pmap/2 (v1.16) |
| v1.20 | Logger.*_backend 함수들 |
:logger_backends 패키지의 LoggerBackends 모듈 |
| v1.20 | Logger.enable/1과 Logger.disable/1 |
각각 Logger.put_process_level/2와 Logger.delete_process_level/1 (v1.15) |
| v1.19 | mix.exs 안 def project의 CLI 설정 |
def cli로 이동 (v1.14) |
| v1.19 | mix do에서 태스크 구분에 , 사용 |
+ 사용 (v1.14) |
| v1.19 | Logger의 :backends 설정 |
Logger의 :default_handler 설정 (v1.15) |
| v1.19 | File.cp/3, File.cp!/3, File.cp_r/3, File.cp_r!/3에 콜백 전달 |
:on_conflict 옵션 (v1.14) |
| v1.18 | EEx에서 <%# |
<%!-- (v1.14) 또는 <% # (v1.0) |
| v1.18 | EEx의 EEx.Engine.handle_text/2 콜백 |
c:EEx.Engine.handle_text/3 (v1.14) |
| v1.18 | Enumerable.slice/1에서 2-arity 함수 반환 |
3-arity 함수 반환 (v1.14) |
| v1.18 | Range.new/2에서 음수 step을 가진 범위 |
범위의 명시적 step (v1.11) |
| v1.18 | Tuple.append/2 |
Tuple.insert_at/3 (v1.0) |
| v1.18 | mix cmd --app APP |
mix do --app APP (v1.14) |
| v1.18 | List.zip/1 |
Enum.zip/1 (v1.0) |
| v1.18 | Module.eval_quoted/3 |
Code.eval_quoted/3 (v1.0) |
| v1.17 | 작은따옴표 charlist ('foo') |
~c"foo" (v1.0) |
| v1.17 | 패턴과 가드의 left..right |
left..right//step (v1.11) |
| v1.17 | ExUnit.Case.register_test/4 |
register_test/6 (v1.10) |
| v1.17 | IO.read/2와 IO.binread/2의 :all |
:eof (v1.13) |
| v1.16 | ~R/.../ |
~r/.../ (v1.0) |
| v1.16 | Enum.slice/2에서 음수 step을 가진 범위 |
범위의 명시적 step (v1.11) |
| v1.16 | String.slice/2에서 음수 step을 가진 범위 |
범위의 명시적 step (v1.11) |
| v1.15 | Calendar.ISO.day_of_week/3 |
Calendar.ISO.day_of_week/4 (v1.11) |
| v1.15 | Exception.exception?/1 |
Kernel.is_exception/1 (v1.11) |
| v1.15 | Regex.regex?/1 |
Kernel.is_struct/2 (Kernel.is_struct(term, Regex)) (v1.11) |
| v1.15 | Logger.warn/2 |
Logger.warning/2 (v1.11) |
| v1.14 | use Bitwise |
import Bitwise (v1.0) |
| v1.14 | ~~~ /1 |
bnot/2 (v1.0) |
| v1.14 | 모듈 본문의 Application.get_env/3 등 |
Application.compile_env/3 (v1.10) |
| v1.14 | String.starts_with?/2의 컴파일된 패턴 |
문자열의 리스트를 대신 전달 (v1.0) |
| v1.14 | Mix.Tasks.Xref.calls/1 |
컴파일 트레이서(Code에 설명됨) (v1.10) |
| v1.14 | Logger의 $levelpad |
없음 |
| v1.14 | 커스텀 연산자로서의 <|> |
다른 커스텀 연산자 (v1.0) |
| v1.13 | Version 요구사항의 !과 != |
~> 또는 >= (v1.0) |
| v1.13 | Mix.Config |
Config (v1.9) |
| v1.13 | mix escript.build의 :strip_beam 설정 |
:strip_beams (v1.9) |
| v1.13 | Macro.to_string/2 |
Macro.to_string/1 (v1.0) |
| v1.13 | System.get_pid/0 |
System.pid/0 (v1.9) |
| v1.12 | ^^^/2 |
bxor/2 (v1.0) |
| v1.12 | 모듈 속성 읽기의 @foo() |
괄호 제거 (v1.0) |
| v1.12 | use EEx.Engine |
대신 EEx.Engine에 명시적으로 위임 (v1.0) |
| v1.12 | Mix의 :xref 컴파일러 |
없음 (이제 컴파일러의 일부로 항상 실행됨) |
| v1.11 | Mix.Project.compile/2 |
Mix.Task.run("compile", args) (v1.0) |
| v1.11 | Supervisor.Spec.worker/3·Supervisor.Spec.supervisor/3 |
Supervisor에 설명된 새 child spec (v1.5) |
| v1.11 | Supervisor.start_child/2·Supervisor.terminate_child/2 |
DynamicSupervisor (v1.6) |
| v1.11 | System.stacktrace/1 |
try/catch/rescue의 __STACKTRACE__ (v1.7) |
| v1.10 | Code.ensure_compiled?/1 |
Code.ensure_compiled/1 (v1.0) |
| v1.10 | Code.load_file/2 |
Code.require_file/2 (v1.0) 또는 Code.compile_file/2 (v1.7) |
| v1.10 | Code.loaded_files/0 |
Code.required_files/0 (v1.7) |
| v1.10 | Code.unload_file/1 |
Code.unrequire_files/1 (v1.7) |
| v1.10 | Logger.log/2에 비-chardata 전달 |
to_string/1로 명시적 문자열 변환 (v1.0) |
| v1.10 | Logger 앱 환경의 :compile_time_purge_level |
Logger 앱 환경의 :compile_time_purge_matching (v1.7) |
| v1.10 | Supervisor.Spec.supervise/2 |
Supervisor에 설명된 새 child spec (v1.5) |
| v1.10 | Supervisor의 :simple_one_for_one 전략 |
DynamicSupervisor (v1.6) |
| v1.10 | Task.Supervisor.start_link/1의 :restart·:shutdown |
Task.Supervisor.start_child/3의 :restart·:shutdown (v1.6) |
| v1.9 | Map.drop/2, Map.split/2, Map.take/2의 Enumerable 키 |
미리 두 번째 인자에 Enum.to_list/1 호출 (v1.0) |
| v1.9 | Mix.Project.load_paths/1 |
Mix.Project.compile_path/1 (v1.0) |
| v1.9 | String.replace/4에 :insert_replaced 전달 |
:binary.replace/4 사용 (v1.0) |
| v1.8 | Collectable.into/1에 비어 있지 않은 리스트 전달 |
++/2 또는 Keyword.merge/2 (v1.0) |
| v1.8 | for/1의 :into에 비어 있지 않은 리스트 전달 |
++/2 또는 Keyword.merge/2 (v1.0) |
| v1.8 | Enum.into/2에 비어 있지 않은 리스트 전달 |
++/2 또는 Keyword.merge/2 (v1.0) |
| v1.8 | 복수형 시간 단위(:seconds, :milliseconds 등) |
단수형 사용(:second, :millisecond 등) (v1.4) |
| v1.8 | Inspect.Algebra.surround/3 |
Inspect.Algebra.concat/2와 Inspect.Algebra.nest/2 (v1.0) |
| v1.8 | Inspect.Algebra.surround_many/6 |
Inspect.Algebra.container_doc/6 (v1.6) |
| v1.9 | Kernel.CLI의 --detached |
--erl "-detached" (v1.0) |
| v1.8 | Kernel.ParallelCompiler.files/2 |
Kernel.ParallelCompiler.compile/2 (v1.6) |
| v1.8 | Kernel.ParallelCompiler.files_to_path/2 |
Kernel.ParallelCompiler.compile_to_path/2 (v1.6) |
| v1.8 | Kernel.ParallelRequire.files/2 |
Kernel.ParallelCompiler.require/2 (v1.6) |
| v1.8 | Mix.Compilers.Erlang.compile/6 콜백에서 {:ok, contents} 또는 :error 반환 |
{:ok, contents, warnings} 또는 {:error, errors, warnings} 반환 (v1.6) |
| v1.8 | System.cwd/0·System.cwd!/0 |
각각 File.cwd/0·File.cwd!/0 (v1.0) |
| v1.7 | Code.get_docs/2 |
Code.fetch_docs/1 (v1.7) |
| v1.7 | Enum.chunk/2,3,4 |
Enum.chunk_every/2와 Enum.chunk_every/3,4 (v1.5) |
| v1.7 | GenServer 콜백에서 super/1 호출 |
super/1을 호출하지 않고 behaviour 명시적 구현 (v1.0) |
| v1.7 | not left in right |
left not in right (v1.5) |
| v1.7 | Registry.start_link/3 |
Registry.start_link/1 (v1.5) |
| v1.7 | Stream.chunk/2,3,4 |
Stream.chunk_every/2와 Stream.chunk_every/3,4 (v1.5) |
| v1.6 | Enum.partition/2 |
Enum.split_with/2 (v1.4) |
| v1.6 | Macro.unescape_tokens/1,2 |
Enum.map/2로 인자 순회 (v1.0) |
| v1.6 | Module.add_doc/6 |
@doc 모듈 속성 (v1.0) |
| v1.6 | Range.range?/1 |
_.._에 패턴 매칭 (v1.0) |
| v1.5 | nil을 뜻하는 () |
nil (v1.0) |
| v1.5 | char_list/0 타입 |
t:charlist/0 타입 (v1.3) |
| v1.5 | Atom.to_char_list/1 |
Atom.to_charlist/1 (v1.3) |
| v1.5 | Enum.filter_map/3 |
Enum.filter/2 + Enum.map/2 또는 for/1 컴프리헨션 (v1.0) |
| v1.5 | Float.to_char_list/1 |
Float.to_charlist/1 (v1.3) |
| v1.5 | GenEvent 모듈 |
Supervisor와 GenServer (v1.0) |
| v1.5 | EEx의 중간·끝 표현식의 <%= |
<% 사용(<%=은 시작 표현식에서만 허용) (v1.0) |
| v1.5 | t:Inspect.Opts.t/0 타입의 :as_char_lists 값 |
:as_charlists 값 (v1.3) |
| v1.5 | t:Inspect.Opts.t/0 타입의 :char_lists 키 |
:charlists 키 (v1.3) |
| v1.5 | Integer.to_char_list/1,2 |
Integer.to_charlist/1과 Integer.to_charlist/2 (v1.3) |
| v1.5 | to_char_list/1 |
to_charlist/1 (v1.3) |
| v1.5 | List.Chars.to_char_list/1 |
List.Chars.to_charlist/1 (v1.3) |
| v1.5 | Module의 @compile {:parse_transform, _} |
없음 |
| v1.5 | Stream.filter_map/3 |
Stream.filter/2 + Stream.map/2 (v1.0) |
| v1.5 | String.ljust/3·String.rjust/3 |
이진 패딩과 함께 String.pad_leading/3·String.pad_trailing/3 사용 (v1.3) |
| v1.5 | String.lstrip/1·String.rstrip/1 |
String.trim_leading/1·String.trim_trailing/1 (v1.3) |
| v1.5 | String.lstrip/2·String.rstrip/2 |
두 번째 인자로 이진을 쓰는 String.trim_leading/2·String.trim_trailing/2 사용 (v1.3) |
| v1.5 | String.strip/1·String.strip/2 |
String.trim/1·String.trim/2 (v1.3) |
| v1.5 | String.to_char_list/1 |
String.to_charlist/1 (v1.3) |
| v1.4 | -> 뒤 표현식이 없는 익명 함수 |
표현식 사용 또는 명시적 nil 반환 (v1.0) |
| v1.4 | private 함수 overridable 지원 | public 함수 사용 (v1.0) |
| v1.4 | 함수 호출로 사용된 변수 | 괄호 사용 (v1.0) |
| v1.4 | Access.key/1 |
Access.key/2 (v1.3) |
| v1.4 | Behaviour 모듈 |
@callback 모듈 속성 (v1.0) |
| v1.4 | Enum.uniq/2 |
Enum.uniq_by/2 (v1.2) |
| v1.4 | Float.to_char_list/2 |
:erlang.float_to_list/2 (Erlang/OTP 17) |
| v1.4 | Float.to_string/2 |
:erlang.float_to_binary/2 (Erlang/OTP 17) |
| v1.4 | HashDict 모듈 |
Map (v1.2) |
| v1.4 | HashSet 모듈 |
MapSet (v1.1) |
| v1.4 | IEx.Helpers.import_file/2 |
IEx.Helpers.import_file_if_available/1 (v1.3) |
| v1.4 | Mix.Utils.camelize/1 |
Macro.camelize/1 (v1.2) |
| v1.4 | Mix.Utils.underscore/1 |
Macro.underscore/1 (v1.2) |
| v1.4 | OptionParser의 다중 문자 alias |
단일 문자 alias 사용 (v1.0) |
| v1.4 | Set 모듈 |
MapSet (v1.1) |
| v1.4 | Stream.uniq/2 |
Stream.uniq_by/2 (v1.2) |
| v1.3 | 문자열/sigil/charlist 안의 \x{X*} |
\uXXXX 또는 \u{X*} (v1.1) |
| v1.3 | Dict 모듈 |
Keyword (v1.0) 또는 Map (v1.2) |
| v1.3 | defdelegate/2의 :append_first 옵션 |
함수 명시적 정의 (v1.0) |
| v1.3 | Enum.group_by/3의 2번째 인자로 맵/딕셔너리 |
Enum.reduce/3 (v1.0) |
| v1.3 | Keyword.size/1 |
length/1 (v1.0) |
| v1.3 | Map.size/1 |
map_size/1 (v1.0) |
| v1.3 | Regex의 /r 옵션 |
/U (v1.1) |
| v1.3 | Set behaviour |
MapSet 데이터 구조 (v1.1) |
| v1.3 | String.valid_character?/1 |
String.valid?/1 (v1.0) |
| v1.3 | Task.find/2 |
직접 메시지 매칭 사용 (v1.0) |
| v1.3 | URI.decode_query/2의 2번째 인자로 비맵 |
맵 사용 (v1.0) |
| v1.2 | Dict behaviour |
Map과 Keyword (v1.0) |
| v1.1 | ?\xHEX |
0xHEX (v1.0) |
| v1.1 | Access 프로토콜 |
Access behaviour (v1.1) |
| v1.1 | alias/2·require/2의 as: true | false |
없음 |
더 알아보기
- 각 버전의 정확한 폐기 내역은 해당 버전의 CHANGELOG(위 링크)에서 확인할 수 있어요.
- Elixir의 릴리즈 정책과 버전 관리에 대한 자세한 내용은 공식 가이드를 참고하세요.
import Kernel, except: [...]로Kernel임포트를 제어하는 방법은Kernel문서에서 다룹니다.