# Elixir 가드의 단락 평가: 조건 순서가 결과를 바꿈

> Clean Markdown view of GeekNews topic #30908. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=30908](https://news.hada.io/topic?id=30908)
- GeekNews Markdown: [https://news.hada.io/topic/30908.md](https://news.hada.io/topic/30908.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-06-28T23:02:03+09:00
- Updated: 2026-06-28T23:02:03+09:00
- Original source: [hauleth.dev](https://hauleth.dev/post/guards-guards/)
- Points: 1
- Comments: 1

## Topic Body

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

## Comments



### Comment 60604

- Author: neo
- Created: 2026-06-28T23:02:04+09:00
- Points: 1

###### [Lobste.rs 의견들](https://lobste.rs/s/b2emi7/guards_guards) 
- Elixir 프로그래머는 아니지만, 마지막 예제에서 가장 놀라운 건 **가드 표현식의 오류**가 호출자에게 전파되지 않고 그 가드가 “건너뛰어진다”는 점임  
  왜 그렇게 만들었는지는 알 것 같지만, 직관에 어긋나는 결과가 나오는 것도 놀랍지 않음

- Erlang의 API 설계가 Armstrong의 [Erlang thesis](https://erlang.org/download/armstrong_thesis_2003.pdf) p109/s4.5에서 말한 **의도적 프로그래밍**을 돕기 위한 것이었다는 점을 생각하면 아이러니함  
  논문에서는 `dict:fetch(Key, Dict)`, `dict:search(Key, Dict)`, `dict:is_key(Key, Dict)`처럼 프로그래머가 “키가 반드시 있어야 한다”, “있을 수도 있으니 흐름을 나눈다”, “존재 여부만 검사한다”는 의도를 드러내는 함수들을 나눠 설명함  
  그런데 Elixir의 `is_map_key/2`는 “dict” 인자가 dict가 아니면 예외를 내고, 그 예외 실패가 전체 가드 절 실패로 이어져 이 구분을 깨는 것처럼 보임  
  반대로 `or`가 예외를 잡아 `false`로 합치는 언어가 있다면 다른 경우에는 더 놀라울 것 같기도 함
  - `is_map_key/2`는 사실 완전히 평범한 **Erlang 함수**임  
    https://www.erlang.org/doc/apps/erts/erlang.html#is_map_key/2

- 예전에 봤던 [이 토론](https://lobste.rs/s/wq1csk/elixir_v1_20_released_now_gradually_typed) 덕분에 이번 퀴즈를 풀 준비가 되어 있었고, 그때 몇 가지를 배웠음
  - 그 토론에서 영감을 받아 이 글을 쓰게 됐음

- 배운 건 있었지만, 왜 **Pratchett 참조**를 피했는지 아쉬움  
  Death가 어디선가 이마를 짚고 있을 듯함  
  여기서 흥미로운 점은 두 가지인데, `false`가 아니라 **실패한 가드**는 전체 표현식을 실패하게 만들고, 다소 직관과 다르게 `is_map_key`가 `is_map` 검사를 내포하지 않는다는 것임  
  `is_map(x) and is_map_key(x, :corporal)`처럼 세 번째 변형을 추가하면 기대한 대로 동작함  
  `is_map_key`의 동작은 조금 일관성이 없어 보이고 그래서 놀랍게 느껴지며, 다른 `is_...` 가드들도 어떤 것은 안전하고 어떤 것은 타입 기대를 깔고 평가해야 하는지 확인해 보면 흥미로울 듯함
  - Pratchett 참조에는 동의하지만, 지금 **폭염**이라 뇌가 기대한 대로 동작하지 않음
  - 궁금해서 몇 가지를 직접 확인해 봤는데, 대략 보기로는 `is_map_key`가 특정 종류의 인자를 요구하는 유일한 **`is_` 가드**인 것 같음  
    다른 `is_` 함수들은 불리언 성격을 내포하고 항상 `true | false`를 반환하며 실패하지 않음

- 여기서 흥미로운 **Elixir 스타일** 질문이 생김  
  예제는 재미있고 설명도 잘 되지만, 개인적으로는 가능하면 가드보다 패턴 매칭을 선호함  
  물론 예외는 있지만, 이런 함수들은 보통 `def a(%{foo: _x}), do: true`, `def a(x) when is_integer(x), do: true`, `def a(_), do: false` 같은 여러 함수 절로 썼을 것 같음

- 함께 볼 만함: https://learnyouahaskell.github.io/syntax-in-functions.html#guards-guards
  - Haskell의 가드는 조금 다름  
    Haskell에서는 가드 안에서 임의의 함수를 호출할 수 있지만, Erlang은 그 안에서 허용되는 함수 집합을 제한함
