Elixir 가드의 단락 평가: 조건 순서가 결과를 바꿈
3 days ago
12
- Elixir의 가드(guard) 에서는 or 조건을 바꾸기만 해도 같은 논리식처럼 보이는 코드의 결과가 달라질 수 있음
- is_integer(x) or is_map_key(x,:foo) 순서는 정수 입력에서 단락 평가가 먼저 일어나 위험한 검사를 건너뜀
- 반대로 is_map_key(x,:foo) or is_integer(x)는 정수 입력에서 첫 조건이 false가 아니라 실패하면서 뒤 조건까지 가지 못함
- 이 차이 때문에 Foo.a(%{foo: 21}), Foo.a(37), Foo.b(%{foo: 21})은 true지만 Foo.b(37)은 false가 됨
- 불리언 연산의 교환법칙이 깨진 것처럼 보이지만, 단락 평가가 있는 or는 원래 조건 순서에 영향을 받으며 Elixir 1.20.1과 OTP 29 기준 경고가 없음
조건 순서가 결과를 바꾸는 예시
- 예시 모듈 Foo는 a/1과 b/1 두 함수를 정의함
- a/1: is_integer(x) or is_map_key(x, :foo) 순서로 가드를 검사함
- b/1: is_map_key(x, :foo) or is_integer(x) 순서로 가드를 검사함
- 가드가 매칭되면 true, 아니면 다음 절에서 false를 반환함
-
a/1: 안전한 조건이 먼저 오는 경우
- Foo.a(%{foo: 21})는 true가 됨
- is_integer(x)는 false
- is_map_key(x, :foo)는 true
- or 결과가 true라 첫 번째 절이 매칭됨
- Foo.a(37)도 true가 됨
- is_integer(x)가 true
- or가 단락 평가되므로 is_map_key(x, :foo)는 실행되지 않음
-
b/1: 실패할 수 있는 조건이 먼저 오는 경우
- Foo.b(%{foo: 21})는 true가 됨
- is_map_key(x, :foo)가 true
- 뒤의 is_integer(x)는 실행되지 않음
- Foo.b(37)은 false가 됨
- 첫 조건 is_map_key(x, :foo)가 false를 반환하는 대신 실패함
- 가드 함수 하나의 실패는 false로 변환되지 않고 전체 가드 표현식을 실패시킴
- 뒤의 is_integer(x)는 호출되지 않고 첫 번째 절도 매칭되지 않음
단락 평가와 경고 부재
- 많은 Elixir 개발자에게 이 동작은 불리언 연산자의 교환법칙이 깨진 것처럼 보일 수 있음
- 하지만 or는 단락 평가를 하므로 두 조건의 위치를 바꿔도 항상 같은 결과가 나온다고 볼 수 없음
- 기준 환경은 Elixir 1.20.1, OTP 29이며, 이 문제에 대해 Elixir가 경고하지 않는 것으로 보임
-
Homepage
-
Tech blog
- Elixir 가드의 단락 평가: 조건 순서가 결과를 바꿈