본문 바로가기

프로그래밍/프로그래밍 언어론

프로그래밍 언어론 독학 1일차

책 제목 : Programming Language Pragmatics (Michael L.Scott 저)



1일차 목표 - 3장 학습 완료하기



3장 - 이름, 범위, 바인딩



*추상화

- 언어의 요소가 어느 특정 컴퓨터 아키텍쳐의 세부적인 특징으로부터 분리된다는 뜻

(특정 명령어 사용 여부, 레지스터 이름, 주소 접근 방식 등 기계적인 속성을 알 필요 없이 용어, 이름, 개념만으로 프로그래밍이 가능하게 해준다는 것이다.)

- 프로그래머가 복잡한 프로그램 부분에 이름을 붙일 수 있게 하는 과정의 뜻도 가지고 있다. 이 때 불필요한 세부사항을 숨겨 개념적 복잡도를 줄일 수 있어 개발자는 코드에 집중 가능하게 된다.



*기계 독립성

- 프로그래밍 언어가 효율적으로 구현되기 위해 특정 명령문 집합에 의존하지 않아야 한다는 의미.



*이름

- 문자열 기호로서 구별되는 것들을 나타내는데 쓰임.

- 대부분의 언어에서 이름은 식별자(identifier)로 표현되며 변수, 상수, 연산, 타입을 포함한 기타 여러 가지를 나타낼 수 있다.

- 심볼 식별자로 표시할 경우 주소같은 낮은 수준의 개념을 이용하지 않아도 된다.



*서브루틴

- 제어 추상화.

- 프로그래머에게 임의의 복잡한 코드 부분을 간단한 인터페이스로 나타낼 수 있게 해준다.



*클래스

- 데이터 추상화.

- 데이터 표현의 세부사항을 상대적으로 간단한 연산의 모양으로 표현하도록 해준다.



*참조 환경

- 프로그램의 특정 시점에서 효과를 발휘하는 모든 바인딩의 집합.



*별칭

- 동일한 범위에서 객체를 참조하는 이름이 하나 이상인 경우를 뜻한다.



*중복

- 한 범위에서 참조되는 문맥에 따라 하나 이상의 다른 이름이 동일 객체를 참조하는 것이다.



*다형성

- 단일 객체가 문맥이나 실행 히스토리에 따라 다른 타임을 가질 수 있게 되는 경우를 뜻한다.



3.1. 바인딩 시점의 개념(notation)



*바인딩

- 두 가지 사물의 결합 (ex. 이름과 이름이 나타내는 대상과의 결합. a = b)

- 바인딩의 시점은 여러 단계에서 이루어진다.



*바인딩 시점

- 이름을 나타내고자 하는 대상과 결합시키는 시점. (바인딩이 생성되는 시점)

- 광범위하게는 언어 구현의 여러 단계에서 구현 결정이 이루어지는 시점.

- 객체의 기억 장소를 할당/해제하는 데 쓰이는 여러 기법에 따라 객체가 유지되는 시간과 바인딩이 유지되는 시간이 구별된다.


1. 언어 설계 시점

- 대부분 언어에서는 흐름 제어, 기본 타입 집합, 복합 타입 생성을 위한 구성요소들이 설계시점에 결정되게 된다.


2. 언어 구현 시점

- 실행 프로그램 구성 방식 등 설계 시점에 정하지 않은 이슈를 언어 구현자의 결정에 맡긴다.


- OS에 따라 입출력을 파일 개념과 연결시키는 것, 스택과 힙의 최대 크기, 실행 시 오버플로우를 처리하는 방식 등이 있다.


3. 프로그램 작성 시점

- 프로그래머는 코딩하며 알고리즘과 자료구조, 이름을 선택한다.


4. 컴파일 시점

- 컴파일러는 고급 구성요소(변수, for문, 함수 등)를 기계어 코드의 명령문과 주소에 대응시키며 변수와 리터럴 값등에 크기와 순서를 정해 메모리에 배치하게 된다.


5. 링크 시점

- 컴파일러는 프로그램의 각 모듈을 따로 컴파일하는 분리 컴파일을 지원하며, 컴파일 시 라이브러리의 표준 서브루틴을 활용한다. 따라서 링커에 의해 모듈이 결합하여 한 덩어리가 되기 전에는 프로그램이 완성되지 못한다.

- 링커는 모듈간의 상대적 위치를 결정하며 참조도 해결한다.

- 한 모듈의 이름이 다른 모듈의 객체를 참조할 경우 링크시점까지 둘 간 바인딩이 최종 결정되지 않는다.

(C언어의 경우 extern으로 선언하거나 다른 파일에 있는 함수를 호출하는 경우이다.)

- 분리 컴파일을 진행할 경우 컴파일 단계에서는 모듈 간 참조에 대해 주소계산을 하지 못하고 비워둔 채 오류여부만 검사한다. 실 주소는 후에 링크 시점에서 채워지게 된다.


6. 로드 시점

- 운영체제가 실행을 위해 프로그램을 메모리에 적재하는 시점.

- 과거 OS에서는 기계 주소를 선택하는 것이 적재 시점까지 결정되지 않았으나, 최근에는 가상 주소와 실 주소를 구별한다.

- 가상 주소는 링크 시점에 정해지며 물리적 주소는 실행 시 바뀔 수 있다.


7. 실행 시점

- 실행의 시작부터 끝까지의 상당히 길고 복잡한 기간을 모두 포함

- 입력, 계산, 지정문으로 변수에 값을 바인딩하는 것은 실행 시점에 이루어지는 가장 중요하고 기본적인 일

- 프르그램의 시작 시간, 모듈에 들어가는 시간, 실현시간(Elaboration Time), 서브루틴 호출 시간, 각 블록에 진입하는 시점, 각 문장의 실행 시점 등을 모두 포함하고 있는 시점이다.

- 예시로는 메모리 할당과 해지, 변수 초기화, 스택 및 심볼테이블 처리가 있다.



*실현 시간(Elaboration Time)

- 변수의 선언이 효력을 발휘하는 영역으로 실제 메모리 공간이 할당되는 시점.

- 함수의 지역변수의 경우 함수가 호출될 때가 실현 시점이라 볼 수 있다.



정적(static)과 동적(dynamic)은 일반적으로 실행 시작 전과 실행 시점에 바인딩 되는 것을 각각 표현한것이다.



*컴파일러 기반 구현

- 컴파일러 기반 언어 구현은 인터프리트 기반 구현보다 더 효율적이다. 좀더 쉽게 결정하기 때문이다.

- 예로 컴파일러는 전역  변수의 구문과 의미를 프로그램 시작 전에 한 번만 분석한다. 또한 선언된 변수들이 메모리에 어떻게 배치될지 결정하고 그 변수들이 나올 때마다 주소로 바로 접근하도록 해줄 수 있다. 하지만 순수 인터프리터의 경우 프로그램 실행시마다 선언을 분석(타입 검사, 메모리 배치, 주소 지정 등)해야한다. 최악은 서브루틴 안에 있는 지역 선언을 서브루틴 호출 시마다 분석하는 경우이다.

- 다중으로 중첩된 반복에서 서브루틴 호출이 발생하면 컴파일러가 선언을 한 번만 분석하여 얻는 시간 절약은 비약적으로 늘어날 수 있다.

- 컴파일러는 컴파일 시점에 지역 변수의 주소를 예측할 수 없다. 함수는 조건식이나 제어 흐름에 따라 다르게 호출될 수 있어 스택이 동적으로 할당되어 지역변수의 메모리 주소는 실행 시점에 호출되고 나서 결정되기 때문이다.

- 컴파일러는 지역변수의 주소를 알 수 없는 대신 그 변수들을 실행 중에 레지스터에 의해 나타내는 기준 위치로부터 상대적인 고정 오프셋으로 표현이 가능하다.



*지연

- 어떤 언어는 정의에 의해 중요한 몇몇 기본 결정이 실행 시점까지 지연되어야 하여 컴파일이 어렵다.

- 언어의 유연성과 표현력을 높이기 위해 지연시키는 경우가 많다.

- 예로 자바의 가상 함수는 동적 바인딩의 부분이다. 실행 시점에서 Object타입의 실제 타입을 알 수 있기 때문이다.(다형성)



*다형성

-하나의 변수 이름이 여러 가지 타입의 객체를 나타낼 수 있다.

- 파이썬의 경우 변수가 선언 없이 사용되고 어떤 타입의 객체든 지정을 통해 가리킬 수 있어 매개변수 타입이 미리 정해지지 않아도 되는 유연성 높은 코드가 작성 가능하다.



목표 달성도

3.1절까지 완료



목표에 대한 평가

너무 과한 범위를 설정했던 것으로 보임.

다음날부터는 범위를 줄여야겠다는 생각을 하였다.

기록을 진행하며 가독성이 더 좋은 작성방법을 찾아봐야겠다.