Design Principles of Programming Languages

Apr 12, 2019


프로그래밍 언어의 설계 원칙

  • 언어 설계의 기본 원칙 : 효율성(Efficiency), 일반성(Generality), 직교성(Orthogonality), 획일성(Uniformity)
  • 기타 설계 원칙 : 간결성(Simplicity), 표현력(Expressiveness), 정확성(Preciseness), 기계 독립성(Machine Independence), 안전성(Security), 기존 표기나 규칙과의 일관성, 확장성(Extensibility), 제약성(restrictability), 부분성(Subset)
  • 일반성, 직교성, 획일성은 매우 밀접한 관계이다.

  • 효율성

    • 목적 코드의 효율성 : 번역기가 효율적인 실행 코드를 생성할 수 있어야함을 의미
      • 번역기의 효율적 실행 코드 생성 -> 최적화
        • ex) Pascal에서 상수는 수식으로 표현되지 않고 번역 과정에서 배정된 값으로 대체된다.
    • 번역의 효율성 : 번역기가 효율적으로 실행 코드를 생성할 수 있어야함을 의미
      • 적절한 크기의 번역기로 빠르게 번역할 수 있는 것
        • ex) 언어 번역의 단계 구성 문제 - Pascal : 단일 패스, Modula-2 : 2 패스
    • 구현 용이성 : 번역기를 효율적으로 작성할 수 있어야 한다.
      • 번역기의 효율적인 구현 문제
        • ex) ALGOL 60은 번역기 구현이 어렵거나 번역 수행 알고리즘이 충분히 이해되지 않아 언어가 성공하지 못했다.
    • 프로그래밍 효율성 : 설계된 언어로 얼마나 빠르고 쉽게 프로그램을 작성할 수 있어야 함을 의미
      • 프로그램 작성의 단순성, 용이성 문제
      • 언어의 표현성, 추상화 메커니즘과 관련
      • 이상적인 언어 - LISP, PROLOG
  • 일반성

    • 특별한 경우를 피하고 밀접하게 관련있는 개념들을 하나의 더 일반적인 것으로 결합하는 성질
    • 대입 연산자(=, :=)가 배열과 레코드를 비롯한 모든 데이터 타입에 적용되는 경우
    • 대입 연산자 활용 예시
    • 비일반성 : 문맥과 관계없는 제한
    • 일반성이 부족한 예
      • 프로시저
        • Pascal : 프로시저 선언과 매개 변수 허용, 프로시저형 변수 불허
        • Modula-2 : 일반성을 갖는다.
        • Ada : 매개 변수에 프로시저를 사용할 수 없다.
      • 배열
        • Pascal : 가변 배열 불허
        • C, Ada : 가변 배열 허용
        • Modula-2, FORTRAN : 가변 배열 전달 능력, 가변 배열 선언 불허
      • 동등 연산자, 배정 연산자 (=, :=) : 대부분의 언어는 배열, 레코드에 적용하지 않지만 Ada는 적용했다.
      • 매개 변수
        • FORTRAN : call-by-reference만 허용
        • ALGOL 68, C, Java : call-by-value, 객체에 대한 포인터를 값으로 전달 가능, 일반성 제공
        • Ada : 일반성 제공
      • 상수
        • FORTRAN : 상수 이름 부재
        • Pascal : 상수 표현에 수식 사용 불가
        • Ada : 일반성 갖춘 상수 제공
    • 일반성이 갖는 문제점
      • 언어의 간결성, 판독성, 신뢰성 저하
      • ex) C언어의 포인터(일반성 제공) - 문제점 제기
        • Java는 포인터 불허를 통해 신뢰성과 판독성 문제 해결
        • Pascal에서는 이명(Aliasing)과 위험을 줄이기 위해 포인터가 본질적으로 제한된다.
  • 직교성

    • ‘직각 또는 완전히 독립적인 방향’을 의미하는 수학의 직교성 새념에서 유래
    • 언어의 구성자들이 각각의 의미를 가진 채 결합하는 성질
    • 한 언어의 구성자가 문맥이 다르다고 다른 의미를 가져서는 안 된다는 성질
    • 구성자 간의 상호작용 또는 문맥의 사용이 예상 밖의 행위를 야기하지 않아야 한다.
    • 매개변수 전달에 있어서 데이터 타입에 상관없이 동일한 전달 방식을 지원하는 경우
    • 비직교성 : 문맥에 의존하는 제한
    • 직교성이 부족한 예
      • 함수 반환값 자료형
        • Pascal : 스칼라형, 포인터형만 허용
        • C : 배열형만 제외
        • Ada : 완벽한 직교성 제공 (모든 자료형 허용)
      • 파일
        • Pascal : 파일형은 특별한 상태 취급 (파일을 프로시저 매개 변수로 전달 금지, 파일 변수는 배정 금지)
        • 대부분의 언어는 파일을 라이브러리로 취급한다.(비직교성 탈피)
      • 문자열
        • Modula-2 : 문자열 배정 (작은 문자열 -> 더 큰 문자열)
        • 크기가 다른 객체에 대한 유일한 배정
      • 매개변수 전달 기법
        • C : 배열 - call-by-reference, 배열 이외의 모든 자료형 - call-by-value
        • Ada : 모든 자료형 - call-by-value, result, value-result 허용(직교성 보장)
    • ALGOL 68의 중요 설계 목표는 직교성 보장이었다.
  • 획일성

    • 비슷한 것은 비슷하게 보이고 비슷한 의미를 가져야 하며, 다른 것은 다르게 보이고 다른 의미를 가져야 한다는 원칙

    • 언어 구조들의 외모와 행동에서의 조화에 중점을 둔다.

    • 획일성이 부족한 예 : C++에서 클래스 정의와 함수 정의

      • 획일성이 부족한 예시
    • 획일성이 부족한 비조화의 예 (Pascal)

      • if문, while문 : begin-end 구조 요구

      • repeat문 : begin-end 구조 비요구

      • 가변 레코드에서 case문, case 제어문 : 구문 상이 (Modula-2에서 해결)

      • 함수 값의 반환 방법 - 배정문과 유사 (타 언어들은 return으로 해결)

        • function f : boolean;
          begin
          	...
          	f := true;
          end;
          
      • ↑ : 포인터 선언(↑integer)과 포인터 값(x↑)에 똑같은 문자로 쓰인다.(Modula-2는 POINTER TO로 해결)

      • 세미콜론(;) : Modula-2, Pascal에서 문장 구분자와 선언 종결자로 사용한다.(C언어에서는 종결자로만 쓰인다.)

        • procedure p; (* terminator *)
          	var x : integer; (* seperator *)
          		y : real; (* seperator *)
          begin
          	x := 0; (* seperator *)
          	y := 1.0; (* seperator *)
          end;  (* terminator *)
          
    • 비획일성은 특별한 문맥에서만 발생되고 구성자들 간의 상호작용을 볼 수 있으므로 비직교성으로 간주될 수도 있다.

  • 간결성

    • 언어가 복잡하지 않고 간결해야함을 의미
    • LISP와 Prolog 등은 단지 몇 개의 기본 구성자를 가지고 있다.
    • Pascal의 주된 설계 원칙은 간결성이다.
    • 직교성, 일반성, 획일성 : 간결성을 보장하지 못한다. (ex : ALGOL 68)
    • 구성자의 수가 적다고 언어가 간결한 것은 아니다. (ex : LISP, PROLOG는 적은 수의 구성자를 가지지만 복잡한 실행 시간과 시스템에 의존적이다.)
    • 과다한 단순성은 언어 사용에 방해되고, 표현력이 부족하며, 많은 제한이 발생된다.
  • 표현력

    • 언어가 복잡한 과정이나 구조를 얼마나 쉽게 표현할 수 있는가를 의미

    • 표현력은 강하나 단순하지 않은 언어 - LISP, PROLOG, ALGOL 68

    • 표현력이 강하면서 단순한 언어 - C

      • ex)

        • while(*s++ == *t++);
          
    • LISP와 ALGOL 60 등의 재귀(Recursion)

      • (defun factorial (n)
            (if (= n 0) 1 (* n (factorial (- n 1)))))
        
    • 표현력은 간결성과 상충될 수 있다.

  • 확장성 (Extensibility)

    • 사용자가 언어에 새로운 기능을 추가할 수 있도록 하는 성질
    • 사용자가 새로운 타입을 정의하는 것, 라이브러리에 새로운 함수를 추가하는 것, 번역기에 새로운 키워드를 추가하는 것 등등
    • 확장성을 가진 언어의 예 - LISP
    • 명령형 언어는 함수형 언어보다 언어 확장이 어려움
    • 추상화 개념(자료 추상화, 제어 추상화)은 확장성을 지원한다.
  • 정확성

    • 프로그램의 실행을 예측할 수 있도록 하는 언어에 대한 정확한 정의가 있는지를 의미한다.
    • 언어에 대한 정확한 정의는 프로그램과 번역기의 신뢰성과 언어의 행위가 예측 가능한 정의의 존재 여부에 영향을 미친다.
  • 기계 독립성

    • 언어가 특정 기계에 의존적이지 않고 독립적인 것을 의미한다.
    • 메모리 할당이나 기계 구조 등의 내용과는 독립적인 미리 정의된 데이터 타입을 제공하는 것.
    • 기계 독립적인 언어 정의를 통하여 보장한다. (호환성 제공)
  • 제약성(Restrictabliity), 부분성(Subset)

    • 언어에 대한 최소한의 지식과 일부 언어 구조만을 알고 있더라도 프로그램을 작성할 수 있다는 성질
    • 언어 제한성의 장점
      • 프로그래머는 언어의 효과적인 사용을 위해 언어 전체를 배울 필요가 없다.
      • 번역기 작성자가 언어 일부분만을 선택하여 구현하고 사용이 가능하다.(부분 언어 지원)
      • ex) SP/1, SP/2, …, SP/K : PL/I의 부분 언어들
  • 보안성(안전성, Security)

    • 프로그래밍 오류를 줄이고 오류 발견을 쉽게 하는 언어를 설계하는 원칙
    • 신뢰성과 정확성에 밀접한 연관을 가지고 프로그래머가 범할 수 있는 오류의 수를 최소화하자는 것
    • 언어 설계 시 자료형, 형 검사, 변수 선언을 도입
  • 기존 표기나 규칙과의 일관성

    • 언어 설계 시 표준화된 특성과 개념을 갖도록 해야 한다.
    • ex) ALGOL 68 - 표준화된 표기를 잘 따르지 않았다. (type 대신 mode 사용)
  • 성공적인 언어 설계
    • 신뢰성
      • 프로그램의 신뢰성을 위해 진단 컴파일러 또는 점검 컴파일러 사용
      • Cornell : PL/I diagnostic, C언어 환경(debugger 포함)
    • 효율적인 번역
      • 초기 고급 언어(FORTRAN, COBOL, …) : 분리 컴파일 제공 -> 효율적 번역 가능, 오류 유발
      • ALGOL 68, Pascal(1970년대 초반) : 신뢰성 강조 -> 통합 컴파일러 -> Ada : 조화 (분리 컴파일의 장점 + 통합 컴파일의 장점), specification part, body part 제공으로 해결
    • 코드 최적화(Optimization)
      • 효율적인 목적 코드
      • 컴파일링 비용 증가
      • 반복 수행부 등 일부분만 최적화 -> 효과 증대
      • 실제 컴파일러들은 여러 최적화 단계를 제공한다.
    • 신뢰성
      • 언어 구문의 과다한 간결성과 생략은 프로그램 판독성을 저하
      • 적절한 수준의 간결성은 프로그래머에게 좋은 훈련이 되며 프로그램의 신뢰성을 증가시킨다.
      • APL과 4세개 언어는 짧은 프로그램이지만 신뢰성을 증진시켰다.
  • C. A. R. Hoare의 성공적인 프로그래밍 언어 설계를 위한 충고
    • 언어의 특정한 특성(feature) 고안
      • 새 특성의 설계자는 한 번에 한 가지 특성에 집중
      • 잘 알려진 언어의 특성 구현
      • 이미 존재하는 언어의 장점을 해치지 않고, 언어의 단점과 불완전성을 해결, 완화한다.
      • 이 특성들이 어떻게 단순하고, 효율적으로 구현되는지 보여야 한다.
      • 사용자 지침서 작성
      • 많은 예제 프로그램들을 작성해서 다른 방법들과 비교 평가한다.
    • 새로운 언어 설계 (기존 특성들을 선택, 조합)
      • 기존의 많은 특성들 : 숙지, 선택, 판단력 구비
      • 특성들 사이의 불일치를 제거하고 중첩 부분을 조정해야 한다.
      • 새 언어의 영역, 목적, 범위, 복잡성, 확장성에 대한 명확한 개념이 필요하다.
      • 실제 구현과 사용자 지침서(초보자용, 고급용)를 제공해야 한다.
    • 새 언어 설계 작업은 단지 기존의 개념을 통합하는 것