Fresh variables

Fresh variables

매크로가 코드를 생성할 때, 생성된 코드 안의 변수가 사용자 코드의 지역 변수와 우연히 충돌하는 일이 생길 수 있어요. 매크로 안에서 전용 변수를 쓰고 싶을 때 %name 문법으로 "신선한 변수(fresh variable)"를 선언할 수 있어요.

출처: Crystal 공식 문서

본문

매크로가 코드를 생성하면, 그 코드는 일반 Crystal 파서로 파싱되는데, 매크로 호출 문맥의 지역 변수가 정의되어 있다고 가정해요.

이것은 예시로 이해하는 게 좋아요.

macro update_x
  x = 1
end

x = 0
update_x
x # => 1

이것은 반복 코드를 피하기 위해 지역 변수를 의도적으로 읽고/쓰는 데 유용할 때도 있지만, 실수로 지역 변수를 덮어쓸 수도 있어요. 이를 피하기 위해 %name으로 fresh variable을 선언할 수 있어요.

macro dont_update_x
  %x = 1
  puts %x
end

x = 0
dont_update_x # outputs 1
x             # => 0

위 예시에서 %x를 쓰면, 현재 스코프의 지역 변수와 절대 충돌하지 않는 이름을 가진 변수를 선언하게 돼요.

추가로, 어떤 다른 AST 노드에 상대적인 fresh variable은 %var{key1, key2, ..., keyN}로 선언할 수 있어요. 예를 들어:

macro fresh_vars_sample(*names)
  # First declare vars
  {% for name, index in names %}
    print "Declaring: ", stringify(%name{index}), '\n'
    %name{index} = {{index}}
  {% end %}

  # Then print them
  {% for name, index in names %}
    print stringify(%name{index}), ": ", %name{index}, '\n'
  {% end %}
end

macro stringify(var)
  {{ var.stringify }}
end

fresh_vars_sample a, b, c

# Sample output:
# Declaring: __temp_255
# Declaring: __temp_256
# Declaring: __temp_257
# __temp_255: 0
# __temp_256: 1
# __temp_257: 2

위 예시에서 세 개의 인덱스 변수를 선언하고 값을 할당한 뒤 출력해서, 각각의 인덱스를 보여주고 있어요.

더 알아보기

  • %x는 사용자 지역 변수와 충돌하지 않는 고유한 이름의 변수를 만들어요.
  • %var{key} 형태를 쓰면 인덱스마다 서로 다른 fresh variable을 만들 수 있어요.
  • 매크로 관련 다른 내용은 매크로 문서에서 확인할 수 있어요.