Programming Language/Theory

[PLT/프로그래밍언어론] 04. Names, Bindings and Scopes

lumana 2024. 11. 19. 22:01

 

04. Names, Bindings and Scopes

Name

: 단순히 다른 객체를 표현하거나 나타내는 일련의 문자. PL에서는 identifier의 형태
name과 object는 같은 것이 아니다. 하나의 이름이 여러 다른 객체, 하나의 객체가 다른 이름을 가질 수 있다.


Denotable Object

: 이름을 부여할 수 있는 객체들

  • 사용자가 부여 : 변수, 매개변수, 함수….
  • PL 부여 : primitive types, primitive operations, pre-defined constnats

Binding

  • : Association(연관성) between a name and a denoted object.
  • 다양한 시점에서 생성될 수 있다
  • static : 디자인, 프로그램 작성, 컴파일타임
  • Dynamic : Runtime

Language Design Bindings

: primitive type, primitive operations. 같은 연산이 다른 언어에서 다른 이름으로 표현됨.


Binding Times(바인딩 시점)

  • Program Writing: 프로그래머가 식별자를 선택하는데, 이는 바인딩의 일부 정의에 해당한다. 이러한 바인딩은 나중에 완성된다.
    • ex) int x = 0; x에 해당하는 메모리 공간이 프로그램 실행 시 할당되고 0이 저장딘다.
  • Compile Time: 컴파일하는 동안, 컴파일러는 글로벌 변수와 같은 일부 데이터 구조에 메모리를 할당
  • Runtime: 아직 생성되지 않은 모든 바인딩을 완료.
    • 지역 변수, 포인터 변수 등등

Referencing Enviornment

  • : 런타임특정 시점에서 name과 Object 사이의 바인딩 집합
  • 우리는 보통 language definition에서 설정하지 않은 것들에 대한 바인딩만 참조한다.

Declaration

: env에서 binding을 도입(introducing)하는 것

  • 예: int x;, // variable declaration. “x가 integer variable의 이름이다” 바인딩
  • int func() { return 0; }, // 이 함수가 실행되는 말든 특정 환경에서 우리는 함수 이름에 대한 바인딩을 introducing 한다.
  • public class Foo;

Single Name - Different Objects

public class Example {
	public int sum;
	public int method() {
		int sum = 0;
		return sum;
	}
}

Single Object - Different Names in Different Environments

서로 다른 env에서는 훨씬 흔하다. Call by reference를 생각하자.


메서드 put() 내부에서, 변수 list는 동일한 ArrayList 객체를 나타낸다. 이 변경은 strings에도 영향을 미친다.


Call by Value는?

값을 복사하여 메서드에 전달한다. 그래서 단일 이름 또는 서로 다른 이름이 나타내는 다른 객체 간의 케이스다.


메서드 put() 내에서 변수 oldStr은 main()의 str과 동일한 값을 가지는 다른 객체를 나타낸다.


Single Object - Different Names in the Same Environment

하나의 객체가 다른 이름을 가지는 경우를 aliasing이라고 하며, 서로 다른 이름들을 별칭(aliases)이라고 함.

Block

env를 이해하기 위해서 먼저 {… }를 생각해보자.

  • block은 시작과 끝을 나타내는 기호로 식별되는 프로그램의 텍스트 영역이다.
  • 해당 region에만 유효한 local declarations을 포함할 수 있다. (외부 영역에는 invalid일 수 있다.)

  • 블록은 여러 방식으로 표현될 수 있다.
  • 보통 블록에 들어가거나 나올 때마다 환경이 변경된다.
  • 블록은 nested(중첩)될 수 있다.
  • 블록의 겹침(overlapping)은 허용되지 않는다.
    • 즉, 이전에 열린 블록을 닫기 전까지 마지막으로 연 블록을 닫을 수 없다.
  • 보통 블록 내부에서 우리는 지역 환경을 고려한다.
  • 블록이 겹치면 매우 복잡해지므로, 이는 어떤 프로그래밍 언어에서도 허용되지 않는다.
  • 그러나 블록 중첩 규칙은 각 프로그래밍 언어에 따라 조금씩 다를 수 있다.

Types of Env

  • Local environment은 블록 내에서 선언된 이름들에 대한 바인딩 집합이다.
  • Non-local enviornment은 블록 내에서 선언되지 않았지만 보이는 이름들에 대한 바인딩 집합이다.
{
	b
	{
		// b가 여기에 선언되어 있지는 않았지만 사용할 수 있다 : Non-local environment
	}
}
  • Global environment은 프로그램이 시작될 때 생성된 바인딩 집합이다.

Visibility Rules

informal 한 개념임. 특정 블록 내에서 이름이 보일지 여부를 결정하는 규칙. 어떤 바인딩이 유효한지 결정

  • 블록 내의 local declaration은 그 블록과 해당 블록 내부의 모든 다른 블록에서 visible하다
  • 동일한 이름의 새로운 선언이 블록 내에서 이루어지면, 그 새로운 선언이 이전 선언을 hide


current block이 바뀌면 env도 바뀌기 때문에, same name이 다른 object에 연결될 수 있다.
a: 모든 블럭에서 보임. 첫번째 b는 1,2,3에서 보임. 두 번째 b는 2에서만. 두번째 b가 첫번째 b를 숨기므로, 블록 2에서는 항상 두번째 변수 b만 나타난다. 블록3에서는 두번째 b가 안보임. 첫번째 b가 보인다.

Environments

  • 변수 a가 전역 변수라고 가정하자.
  • 그러면 a는 모든 블록에서 보이며, 전역 환경의 일부가 된다.
  • 블록 1의 경우, a의 바인딩은 global 및 non-local environment의 일부가 된다.



  • local env의 name은 inner block에서 보이지만, outer block에서는 보이지 않는다.
  • non-local env의 namelocal env의 같은 name에 의해 숨겨진다.
    • 더 정확히는, 첫 번째 변수 b의 바인딩은 블록 2의 지역 환경에서 비활성화(deactivated)된다.
  • 이런 것들은 PL마다 다르다. 자바에서는 중복 지역 변수가 안되고, 전역 변수 오버라이드는 됨.

Scope Rule

Visibility rule을 scope rule 이라고도 부른다.


Static vs Dynamic

  • Static scope(또는 lexical scope)는 오직 프로그램의 syntatic 구조에만 의존한다.
    • 따라서 env는 컴파일러에 의해 완전히 결정될 수 있다.
  • Dynamic scope프로그램의 실행 과정을 되돌아가며(backward) binding을 결정한다.
    • 따라서 runtime에 결정될 수 있다.

Static Scope Rule

: 가장 가까운 중첩된 스코프의 규칙(the rule of the nearest nested scope)으로 볼 수 있다.

  1. 블록 내의 선언은 해당 블록의 지역 환경을 정의한다.
  2. 블록 내에서 이름이 사용되면, 해당 이름의 유효한 바인딩은 지역 환경에 있는 것이다. 만약 없다면, 가장 가까운 외부 블록의 것을 찾는다.
    • local env에서 먼저 찾고 없으면 가장 neartest outer block에서 찾는다는 것.
  3. 블록 자체가 이름과 연관될 수 있으며, 이 이름들은 블록의 지역 환경의 일부가 된다.

Rule 1: Local Declaration

지역적으로 선언된 변수는 지역 환경을 정의한다.

  • 지역적으로 선언된 변수는 지역 환경을 정의한다.
  • 블록 1의 경우, 오직 변수 b만이 이 블록에서 선언되었다.
  • 다른 변수들은 보이거나 보이지 않거나, 보이더라도 지역 환경에 포함되지 않는다.
    • a는 block 1에서 non-local임

Rule 2: Nearest Nested Scope

  • 블록 3에서 변수 a가 참조된다.
  • 하지만 이 블록에서는 선언되지 않았다.
  • 규칙 2에 따라, 우리는 먼저 블록 1을 찾는다.
  • 여전히 찾지 못하면 블록 0을 찾는다 ➞ 여기에서 a가 선언되었다.
  • 주의할 점은, 우리는 "중첩된(“nested”)” 블록만을 검색하기 때문에 블록 2는 건너뛴다.

Rule 3: Names assigned to Block

  • 자바 코드에서, 메서드 이름 put, 매개변수 liststr은 실제로 블록 내에 있지 않다.
  • 그러나 이들은 지역 환경으로 제공된다.
  • 또한, 이들은 외부 블록에서 보이지 않는다. 왜냐하면 지역 환경의 일부이기 때문이다.
    • put()은 예외로, 이는 선언이 포함된 블록에 보이는 절차이기 때문이다.

Static Scope의 장점

  • 모든 정적 스코프 규칙은 pre-defined이고, 코드의 syntatic 구조에만 의존한다.
  • 컴파일러는 사용된 이름의 모든 바인딩을 deduce(추론)할 수 있다.
  • 이 사실은 큰 장점을 제공한다:
    • 우리는 프로그램을 더 잘 이해할 수 있다.
    • 컴파일러는 올바름 검사를 수행할 수 있다.
    • 컴파일러는 상당한 최적화를 수행할 수 있다.
      • 변수 바인딩을 예측할 수 있으므로

정리하자면 Static은 지역 환경 가장 가까운 outer block + 블록에 이름이 할당되는 규칙


Dynamic Scope


  • 프로그램의 특정 지점 P에서 이름 X의 유효한 바인딩은 X의 가장 최근에 생성된 바인딩(the most recently created binding)이다.
  • X는 여전히 지점 P에서 활성 상태여야 한다.

  • static scope rule을 사용하여 코드를 고려해보자:
    • 1번째 줄의 x는 전역 변수이다.
    • 10번째 줄에서 함수 bar가 호출된다.
    • bar는 함수 foo를 호출한다.
    • 함수 foo3번째 줄에서 1을 출력한다 ➞ 1번째 줄의 x를 사용한다.
    • 그런 다음 11번째 줄에서 다시 x를 출력한다 ➞ x는 4번째 줄에서 변경되었다.
      • 따라서 2를 출력한다.

헷갈리지 말자. 중첩되서 선언된게 아님. foo에서 bar의 local x를 볼 수 없음.


  • Dynamic scope rule을 사용하여 코드를 봐보자.
  • 동적 스코프에서는 이 스크립트의 실제 출력은 다음과 같다:
    • 3 (3번째 줄에서 출력됨)
    • 1 (11번째 줄에서 출력됨)
    • 3번째 줄에서는 이름 x의 가장 최근 바인딩이 7번째 줄에 있다.
      • 따라서 3을 출력한다.
    • 11번째 줄에서는 x의 가장 최근 바인딩(4번째 줄의 2)은 이미 사라졌다.
      • (디테일하게 말하면 foo와 bar는 이미 call stack에서 pop 되버림.)
      • bar가 이미 끝남
      • x가 local로 바인딩 된 곳에 2가 저장됬고, 이 로컬이 bar()가 끝나면서 사라짐.
    • 따라서 1을 출력한다.

동적 스코프는 함수 호출 시점에서 가장 가까운 스코프에서 결정됨(call stack). 정적 스코프는 함수가 정의된 시점에서 스코프가 결정된다. Local 먼저 찾고 그다음 가까운 outer로 간다. 코드 레벨.


동적 스코프의 장점

  • 프로그램 재작성 없이 동작을 쉽게 수정할 수 있다.
  • 단점 : 코드 이해가 굉장히 어려워진다.