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가 경고하지 않는 것으로 보임
Read Entire Article