Simula

Simula는 1960년대 노르웨이 컴퓨팅 센터에서 올레요한 달과 크리스텐 뉘고르가 개발한 프로그래밍 언어 계열이다. 이 이름은 이산 사건 시뮬레이션을 위해 설계된 Simula I과 이를 범용 언어로 확장한 Simula 67을 함께 가리킨다. Simula는 ALGOL 60의 블록 구조와...

분류범용 프로그래밍 언어, 시뮬레이션 언어, 객체 지향 프로그래밍 언어
패러다임명령형 프로그래밍, 절차형 프로그래밍, 구조적 프로그래밍, 객체 지향 프로그래밍
설계자올레요한 달, 크리스텐 뉘고르
개발 기관노르웨이 컴퓨팅 센터
개발 시기1962년~1967년
주요 판Simula I, Simula 67
선행 언어ALGOL 60, SIMSCRIPT
타입 체계정적 타입, 명목적 타입
주요 구현GNU Cim, Portable Simula
영향을 준 언어Smalltalk, C++, Java, C#, Eiffel, BETA

Simula는 1960년대 노르웨이 컴퓨팅 센터에서 올레요한 달크리스텐 뉘고르가 개발한 프로그래밍 언어 계열이다. 이 이름은 이산 사건 시뮬레이션을 위해 설계된 Simula I과 이를 범용 언어로 확장한 Simula 67을 함께 가리킨다. Simula는 ALGOL 60의 블록 구조와 절차적 문법을 바탕으로 클래스, 객체, 상속, 가상 절차, 동적 객체 생성과 같은 구조를 하나의 언어 체계로 결합했다.[1]

Simula I은 복잡한 시스템을 여러 개의 상호작용하는 프로세스로 표현하고 실행하기 위한 시뮬레이션 언어로 개발되었다. 1962년에 언어 설계가 시작되었으며, 최초의 컴파일러는 UNIVAC 1107ALGOL 60 컴파일러를 확장하는 방식으로 구현되어 1964년 말부터 동작하기 시작했다.[2] 이후 개발된 Simula 67은 시뮬레이션에 한정되지 않는 범용 프로그래밍 언어로 재설계되었고, 프로그램의 데이터와 동작을 클래스 단위로 정의한 뒤 실행 중 객체를 생성하는 구조를 제공했다.

Simula 67은 일반적으로 최초의 객체 지향 프로그래밍 언어로 평가된다. 언어가 개발되던 당시에는 아직 ‘객체 지향 프로그래밍’이라는 용어가 정착하지 않았지만, Simula 67은 캡슐화된 클래스, 클래스 간 상속, 가상 절차를 통한 후기 바인딩, 실행 중 객체 생성이라는 현대 객체 지향 언어의 핵심 요소를 함께 제시했다. 이러한 구조는 실제 세계의 사람, 기계, 작업, 사건과 자원 등을 프로그램 내부의 독립된 객체로 대응시켜 복잡한 시스템을 모델링하기 위해 만들어졌다.[3]

Simula의 객체 모델은 이후 Smalltalk의 순수 객체 지향 환경, C++의 클래스 기반 시스템 프로그래밍, JavaC#의 객체 모델 등에 직접적이거나 간접적인 영향을 주었다. 또한 클래스와 객체를 단순한 자료 구조가 아니라 프로그램을 구성하고 현실의 개념을 표현하는 기본 단위로 사용한다는 관점은 이후 소프트웨어 설계 방법론과 객체 지향 분석·설계의 형성에도 중요한 기반이 되었다.[4]

역사

개발 배경

Simula의 기원은 1950년대 노르웨이 국방연구소에서 수행된 운용 연구와 시뮬레이션 작업에 있다. 크리스텐 뉘고르는 1948년부터 노르웨이 국방연구소에서 계산 업무에 참여했으며, 1949년부터 1950년 사이에는 노르웨이 최초의 원자로 건설과 관련된 공명 흡수 계산에 몬테카를로 방법을 적용했다. 그는 이후 운용 연구를 담당하면서 군사·산업 시스템처럼 구성 요소가 많고 상호작용이 복잡한 대상을 분석하려면 단순한 수치 계산만으로는 충분하지 않으며, 시스템 자체의 구조와 시간에 따른 작동을 표현할 수 있는 개념 체계가 필요하다고 보았다.[5]

1950년대 후반에는 기존 컴퓨터에서 현실적인 규모의 운용 연구 모델을 작성하는 일이 점차 어려워졌다. 당시의 시뮬레이션 프로그램은 대개 특정 문제에 맞추어 직접 작성되었으며, 서로 다른 종류의 사람, 장비, 작업, 대기열과 사건을 하나의 일관된 방식으로 표현하기 어려웠다. 뉘고르는 복잡한 시스템을 구성하는 요소와 요소 사이의 상호작용을 프로그램의 구조에 직접 대응시키는 언어가 필요하다고 판단했다. 이 구상은 처음부터 단순한 계산용 언어가 아니라, 시스템을 설명하는 표현 수단과 실제 실행 가능한 프로그래밍 언어를 결합하려는 시도였다.[6]

뉘고르는 1960년 5월 노르웨이 국방연구소를 떠나 노르웨이 컴퓨팅 센터의 연구 활동을 확장하는 일을 맡았다. 민간 운용 연구에서도 군사 연구와 비슷하게 시뮬레이션 모델을 구성할 언어와 도구가 부족하다는 문제가 반복되었고, 이러한 경험은 1961년 Simula 개발을 직접 시작하게 된 계기가 되었다. Simula라는 이름이 문서에 처음 등장한 시점은 1962년 1월 5일로 알려져 있다.[7]

올레요한 달은 노르웨이 국방연구소에서 초기 컴퓨터와 프로그래밍 언어 구현을 연구하고 있었다. 뉘고르는 시스템 모델링과 운용 연구에 대한 경험을 가지고 있었지만, 새로운 언어를 실제로 설계하고 구현하기 위해서는 컴파일러와 프로그래밍 언어에 관한 전문 지식이 필요했다. 달은 1962년 초부터 뉘고르의 언어 설계 작업에 본격적으로 참여했고, 1963년 3월 노르웨이 컴퓨팅 센터로 자리를 옮겼다. 두 사람은 복잡한 시스템을 표현하려는 뉘고르의 모델링 관점과 달의 언어 구현 능력을 결합해 Simula를 공동 개발했다.[8]

ALGOL 60의 선택

초기 Simula 구상은 특정한 구현 언어를 전제로 하지 않았지만, 달과 뉘고르는 곧 ALGOL 60을 기반 언어로 선택했다. ALGOL 60은 재귀 호출이 가능한 블록 구조, 지역 변수, 절차, 조건문과 반복문 등 당시로서는 체계적인 프로그램 구조를 제공했다. Simula는 이러한 문법과 실행 모델을 유지하면서 시뮬레이션을 위한 새로운 개념을 추가하는 방향으로 설계되었다.[9]

ALGOL 60을 채택한 것은 문법을 차용하는 것 이상의 의미가 있었다. ALGOL의 블록은 선언된 변수와 절차를 하나의 범위 안에 묶고, 블록이 실행될 때마다 대응하는 동적 인스턴스를 생성했다. 달과 뉘고르는 이 블록 인스턴스를 프로그램 실행이 끝날 때까지 독립적으로 유지하고, 다른 블록에서 참조하며, 실행을 중단했다가 다시 시작할 수 있도록 확장했다. 이 확장은 뒤에 Simula의 프로세스와 객체 개념으로 발전했다.[10]

초기 언어 구상

1961년부터 1962년까지의 초기 설계에서는 시뮬레이션 대상을 이산 사건 네트워크로 표현했다. 시스템은 서비스를 제공하는 station과 그 사이를 이동하는 customer로 구성되었으며, 프로그램은 customer가 station에 도착하고 대기하며 서비스를 받은 뒤 다른 station으로 이동하는 과정을 실행했다. 이 구조는 공항, 생산 설비, 통신망과 대기 행렬처럼 흐름이 비교적 명확한 시스템을 모델링하는 데 적합했다.[11]

그러나 station은 능동적으로 작동하고 customer는 수동적으로 이동한다는 구분이 모든 모델에 자연스럽게 적용되지는 않았다. 어떤 모델에서는 이동하는 대상이 능동적으로 행동해야 했고, 고정된 설비는 오히려 수동적인 자원으로 다루는 편이 적절했다. 달과 뉘고르는 이 두 종류를 별도의 언어 요소로 유지하는 대신, 각각 독립된 실행 상태를 가지며 서로 상호작용하는 일반적인 process 개념으로 통합했다. 이에 따라 시스템은 준병렬적으로 실행되는 여러 프로세스의 집합으로 표현되었다.[12]

프로세스는 실행 도중 다른 프로세스에 실행권을 넘기고, 나중에 중단된 위치에서 다시 실행될 수 있었다. 실제로 여러 프로세스가 동시에 실행되는 것은 아니었지만, 모델을 작성하는 사람은 각 구성 요소가 자체적인 생명 주기와 행동 순서를 가진 것처럼 기술할 수 있었다. Simula에서는 이러한 실행 방식을 준병렬 실행이라고 불렀으며, 이후 코루틴과 객체의 동작을 결합하는 기반이 되었다.

Simula I

1962년 5월 뉘고르는 미국에서 열린 UNIVAC의 신형 컴퓨터 홍보 행사에 초청되었다. 그는 이 방문을 Simula 구상을 알리는 기회로 이용했고, UNIVAC의 시스템 프로그래밍 책임자였던 로버트 베머에게 언어 설계를 소개했다. 베머는 이 구상에 관심을 보였으며, 뉘고르를 1962년 8월 뮌헨에서 열린 IFIP 세계 회의의 세션에 초청했다. 뉘고르는 이 회의에서 「SIMULA—an Extension of ALGOL to the Description of Discrete-Event Networks」를 발표했다.[13]

노르웨이 컴퓨팅 센터는 UNIVAC과의 계약을 통해 UNIVAC 1107을 도입했으며, Simula 컴파일러를 해당 시스템에 제공하기로 했다. 컴파일러는 기존 UNIVAC 1107용 ALGOL 60 컴파일러를 확장하는 방식으로 개발되었다. 노르웨이 컴퓨팅 센터는 1963년 8월 UNIVAC 1107을 인도받았고, 달과 개발진은 언어 설계와 컴파일러 구현을 병행했다.[14]

Simula I 컴파일러의 첫 번째 동작 가능한 판은 1964년 12월 완성되었으며, 전체 시스템은 1965년 1월부터 사용할 수 있게 되었다. 언어 정의와 사용자 설명서는 같은 해 5월 출판되었다. Simula I은 ALGOL 60의 일반 계산 기능에 프로세스, 사건 순서, 시뮬레이션 시간, 대기열과 난수 분포 등을 결합한 이산 사건 시뮬레이션 언어였다.[15]

Simula I의 activity 선언은 같은 종류의 프로세스가 공유하는 데이터와 동작을 정의했다. 다음 코드는 1965년 언어 정의에 수록된 자동차 모델을 읽기 쉽도록 다시 배열한 것이다. 자동차는 속도 V, 기준 위치 X0, 기준 시간 T0를 상태로 가지며, X 절차는 현재 시뮬레이션 시간에 따른 위치를 계산한다.

activity Car;
begin
    real V, X0, T0;

    real procedure X;
        X := X0 + V * (time - T0);

    procedure Update(VNew);
        real VNew;
    begin
        X0 := X;
        T0 := time;
        V := VNew
    end
end;

이 코드에서는 데이터와 이를 처리하는 절차가 하나의 activity 안에 함께 정의되어 있다. Car의 각 인스턴스는 독립적인 위치와 속도를 가지고, Update를 호출해 특정 시점부터의 운동 상태를 변경할 수 있었다. 아직 Simula 67의 class 문법은 존재하지 않았지만, 상태와 동작을 하나의 모델링 단위에 묶는 방식은 이후 객체의 직접적인 전신이 되었다.[16]

1965년 노르웨이 컴퓨팅 센터에서는 세 차례의 Simula I 교육 과정이 열렸고, 언어는 실제 운용 연구 프로젝트에 적용되기 시작했다. 스웨덴에서는 전력·산업 설비와 생산 공정의 시뮬레이션에 사용되었고, 이후 독일, 미국과 소련 등으로 알려졌다. UNIVAC 1100 계열 외에도 Burroughs B5500과 소련의 URAL-16을 위한 구현이 개발되었으며, Simula I 설명서는 러시아어로 번역되었다.[17]

Simula I의 사용 경험은 프로그램을 실행해 수치 결과를 얻는 것뿐 아니라 프로그램을 작성하는 과정 자체가 시스템 분석에 유용하다는 점을 보여주었다. 모델의 구성 요소를 프로세스로 분리하고 상호작용을 명시하면, 분석자는 실제 시스템의 구조를 더 정확히 이해할 수 있었다. 달과 뉘고르는 Simula를 특정 시뮬레이션 문제만 해결하는 언어가 아니라, 복잡한 시스템을 기술하고 이해하는 일반적인 방법으로 확장할 수 있다고 판단했다.

Simula I에서 Simula 67로

1965년과 1966년 동안 달과 뉘고르는 Simula I을 교육하고 실제 프로젝트에 적용하는 한편, 언어를 더 일반적인 형태로 재설계하기 시작했다. Simula I의 프로세스는 시뮬레이션 실행에는 유용했지만, 데이터 구조와 절차를 정의하고 이를 여러 종류의 모델에 재사용하는 일반적인 언어 요소로는 제약이 있었다. 특히 서로 비슷한 프로세스들이 공통 속성을 공유하면서 일부 동작만 다르게 정의할 수 있는 구조가 필요했다.[18]

이 시기에 영국의 컴퓨터 과학자 C. A. R. 호어가 제안한 레코드 클래스와 참조 개념도 설계에 영향을 주었다. 달과 뉘고르는 ALGOL 블록을 여러 번 인스턴스화할 수 있는 데이터·동작 패턴으로 확장하고, 각 인스턴스를 참조값으로 다룰 수 있도록 했다. 또한 기존 클래스의 정의를 새 클래스 앞에 결합하는 prefixing 구조를 도입했다. 이 구조는 뒤에 상속으로 불리게 되는 클래스 계층의 기반이었다.[19]

1966년 발표된 「SIMULA—An ALGOL-Based Simulation Language」에서는 Simula I의 프로세스 중심 모델과 시스템 기술 언어라는 목표가 정리되었다. 같은 시기에는 후속 언어를 가리켜 잠정적으로 ‘Simula II’라고 부르기도 했으나, 클래스와 하위 클래스 개념이 설계의 중심으로 자리 잡으면서 새로운 언어는 Simula 67이라는 이름으로 정리되었다.[20]

클래스와 객체의 형성

Simula 67에서 클래스는 데이터 필드만을 정의하는 레코드가 아니라, 데이터와 해당 데이터를 사용하는 절차 및 실행 동작을 함께 정의하는 프로그램 패턴이었다. 클래스 선언을 실행해 생성된 동적 인스턴스는 객체라고 불렸다. 객체는 자체적인 지역 데이터와 동작을 가지며, 참조를 통해 다른 객체와 연결될 수 있었다.[21]

1970년판 Common Base Language에 수록된 다음 예시는 주문을 하나의 클래스로 정의한다. new Order(103)은 주문 번호가 103인 새로운 객체를 동적으로 생성한다.

class Order(Number);
    integer Number;
begin
    integer NumberOfUnits, ArrivalDate;
    real ProcessingTime
end;

ref(Order) CurrentOrder;
CurrentOrder :- new Order(103);

초기 문서에서는 객체 생성을 단순히 new Order(103)으로 제시했으며, 위 코드의 ref(Order) 변수와 참조 대입은 생성된 객체를 이후에도 사용할 수 있도록 완성한 형태다. Simula는 일반 값 대입에 :=를 사용하고 객체 참조 대입에 :-를 사용해 두 연산을 구분했다.

기존 클래스를 다른 클래스의 접두부로 사용하면 기존 속성을 포함하는 새로운 클래스가 만들어졌다. Common Base Language의 주문 예시는 다음과 같이 계층을 구성한다.

Order class BatchOrder;
begin
    integer BatchSize;
    real SetupTime
end;

Order class SingleOrder;
begin
    real SetupTime, FinishingTime, Weight
end;

SingleOrder class Plate;
begin
    real Length, Width
end;

BatchOrderSingleOrderOrder에 선언된 주문 번호, 수량, 도착일과 처리 시간을 포함하며, 각 클래스에 고유한 속성을 추가한다. Plate는 다시 SingleOrder를 접두 클래스로 사용하므로 OrderSingleOrder의 속성을 모두 이어받는다. 현대 언어의 문법으로는 BatchOrder extends Order, Plate extends SingleOrder에 해당하는 구조다.[22]

Simula 67은 여기에 가상 절차를 추가했다. 상위 클래스는 호출할 절차의 이름과 형식을 선언하고, 하위 클래스가 실제 구현을 제공할 수 있었다. 객체가 참조를 통해 사용될 때 실제 객체의 클래스에 정의된 절차가 선택되었으며, 이는 후기 바인딩과 하위형 다형성의 초기 형태였다. 클래스, 하위 클래스, 참조, 동적 생성과 가상 절차가 하나의 언어 안에 결합되면서 오늘날 클래스 기반 객체 지향 프로그래밍의 핵심 구조가 형성되었다.

Simula 67의 확정

달과 뉘고르는 1967년 5월 오슬로에서 열린 IFIP 시뮬레이션 프로그래밍 언어 워킹 콘퍼런스에서 클래스와 하위 클래스 선언에 관한 논문을 발표했다. 이 발표에서는 클래스 인스턴스, 접두 클래스, 참조와 가상 속성 등 Simula 67의 중심 요소가 정리되었다.[23]

같은 시기 Control Data Corporation은 자사의 CDC 3000 및 CDC 6000 계열 컴퓨터에 Simula를 구현하는 데 관심을 보였다. 여러 조직이 서로 호환되는 컴파일러를 만들려면 구현 이전에 공통 언어 정의가 필요했기 때문에, 노르웨이 컴퓨팅 센터는 Simula 67 Common Base를 마련하기로 했다. 1967년 5월 23일에는 Control Data와 노르웨이 컴퓨팅 센터 사이에 구현 계약이 체결되었다.[24]

1967년 6월 5일부터 9일까지 노르웨이 컴퓨팅 센터에서 Simula 67 Common Base Conference가 열렸다. 노르웨이 컴퓨팅 센터, Control Data, 오슬로 대학교와 관련 구현 조직의 대표들이 참석해 모든 Simula 67 컴파일러가 공통으로 제공해야 할 기능을 결정했다. 이 회의에서는 일부 쟁점을 이후 검토 대상으로 남겼으며, 언어 정의와 구현 호환성을 관리하기 위한 Simula Standards Group이 설립되었다. 뉘고르는 초대 의장을 맡았다.[25]

언어 설계는 1967년 가을 동안 문자열 처리와 입출력 기능을 포함하도록 보완되었다. 비외른 뮈르하우그는 문자열과 입출력 체계의 설계에 크게 기여했고, 이에 따라 달·뮈르하우그·뉘고르가 Simula 67 Common Base Language의 공동 저자가 되었다. 언어의 실질적인 동결은 1967년 가을에 이루어졌으며, 형식적인 확정은 1968년 2월 10일 오슬로에서 열린 Simula Standards Group 회의에서 이루어졌다.[26]

Common Base Language와 표준화

1968년 5월 노르웨이 컴퓨팅 센터는 달, 뮈르하우그와 뉘고르가 작성한 《SIMULA 67 Common Base Language》를 출판했다. 이 문서는 모든 Simula 67 컴파일러가 구현해야 하는 언어 기능을 규정한 참조 문서였다. 일반적인 교재나 입문 설명서가 아니라 문법, 타입, 클래스, 참조, 객체 수명, 원격 접근, 준병렬 실행과 표준 클래스의 의미를 정밀하게 정의하는 사양의 성격을 가졌다.[27]

Common Base는 Simula 67을 특정 시뮬레이션 전용 언어가 아니라 범용 프로그래밍 언어로 규정했다. 시뮬레이션 기능은 언어 문법에 직접 고정되지 않고, 연결 리스트를 제공하는 SIMSET과 이산 사건 실행 모델을 제공하는 SIMULATION이라는 시스템 클래스로 구성되었다. 사용자는 필요한 시스템 클래스를 프로그램의 접두부로 지정해 일반 Simula를 시뮬레이션 언어로 확장할 수 있었다. 이는 핵심 언어 위에 문제 영역별 추상화를 구축하는 초기 프레임워크 구조로 볼 수 있다.

1970년 5월 Simula Standards Group은 수정 사항과 명확화 내용을 반영한 Common Base를 승인했고, 같은 해 10월 개정판이 출판되었다. 개정판이 나올 무렵에는 CDC 3300, CDC 3600, CDC 6600, UNIVAC 1108과 IBM System/360·370 등 여러 컴퓨터 계열에서 Simula 컴파일러를 사용할 수 있었다.[28]

초기 구현과 확산

Simula 67의 초기 구현은 언어 설계와 병행해 진행되었다. Control Data의 CDC 6000 계열 구현은 파리에서 개발되었고, CDC 3000 계열 구현은 오슬로 대학교와 Kjeller Computer Installation의 공동 프로젝트로 진행되었다. 두 CDC 계열 컴파일러는 1969년 봄까지 완성되었다. 노르웨이 컴퓨팅 센터는 이후 UNIVAC 1100 계열과 IBM System/360·IBM System/370용 컴파일러를 개발하고 배포했다.[29]

스웨덴 국방연구소에서 개발한 DECsystem-10용 Simula 구현도 언어 확산에 중요한 역할을 했다. Simula는 1970년대에 유럽의 대학과 연구기관을 중심으로 프로그래밍 교육, 운용 연구, 컴퓨터 그래픽스, 공정 제어와 시스템 모델링에 사용되었다. 사용자와 구현 기관 사이의 협력을 위해 Association of Simula Users가 1972년 설립되었으며, 노르웨이 컴퓨팅 센터는 같은 해부터 Simula Newsletter를 정기적으로 발행했다.

Simula Standards Group은 여러 구현 사이의 차이를 조정하면서 언어 정의를 계속 관리했다. 이후 표준에는 객체 속성에 대한 접근 제어를 강화하는 hidden, protected 등의 기능이 반영되었고, 1986년에 정리된 Simula Standard는 후기 Simula 구현의 공통 기준이 되었다.

역사적 위치

Simula의 개발은 처음부터 객체 지향 프로그래밍이라는 이름의 패러다임을 만들기 위해 시작된 것은 아니었다. 달과 뉘고르가 해결하려던 문제는 현실의 복잡한 시스템을 프로그램 안에서 이해할 수 있는 단위로 나누고, 각 단위가 자체 상태와 행동을 가지며 다른 단위와 상호작용하도록 표현하는 것이었다. 이 문제를 해결하는 과정에서 프로세스가 클래스와 객체로 일반화되었고, 접두 클래스는 상속으로, 가상 절차는 동적 디스패치로 발전했다.

Simula 67은 클래스와 객체 중 하나를 처음 사용한 언어라는 사실만으로 중요한 것이 아니다. 클래스 선언, 동적 객체 생성, 객체 참조, 클래스 계층, 상속, 가상 절차와 객체 생명 주기를 하나의 일관된 실행 모델로 결합했다는 점에서 최초의 클래스 기반 객체 지향 프로그래밍 언어로 평가된다. Computer History Museum 역시 Simula가 데이터와 명령을 시뮬레이션 대상에 대응하는 객체로 묶었다는 점을 초기 객체 지향 언어의 핵심으로 설명한다.[30]

Simula의 직접적인 상업적 보급 규모는 이후의 C++, JavaC#보다 작았지만, 언어가 제시한 모델은 프로그래밍 언어와 소프트웨어 설계의 방향을 바꾸었다. Simula의 역사는 시뮬레이션을 위한 문제 영역별 언어가 범용 객체 지향 언어로 확장되고, 그 언어의 추상화 방식이 이후 수십 년 동안 소프트웨어 개발의 중심 구조로 자리 잡은 과정으로 볼 수 있다.

언어 설계

Simula 67은 ALGOL 60의 문법과 실행 구조를 확장한 범용 프로그래밍 언어이다. ALGOL 60으로 작성된 대부분의 프로그램은 약간의 수정만으로 Simula에서 사용할 수 있었으며, 기본적인 블록 구조, 변수와 배열, 절차, 조건문, 반복문과 재귀 호출도 그대로 유지되었다. Simula가 추가한 핵심 요소는 클래스와 객체, 객체 참조, 접두 클래스를 통한 상속, 가상 절차, 동적 객체 생성, 객체 수명 관리와 준병렬 실행이다.[31]

이러한 기능은 서로 독립적으로 덧붙은 문법이 아니라 하나의 실행 모델을 이룬다. 클래스는 ALGOL 블록을 반복해서 생성할 수 있는 패턴으로 일반화하고, 객체는 실행 중 생성되어 블록 실행이 끝난 뒤에도 존재할 수 있는 클래스 인스턴스로 정의된다. 참조는 객체를 다른 객체와 연결하며, 접두 클래스와 가상 절차는 공통 구조를 공유하면서도 객체 종류에 따라 동작을 바꿀 수 있게 한다. 준병렬 실행은 객체가 자신의 실행 지점을 보존한 채 다른 객체에 실행권을 넘겼다가 나중에 다시 실행되도록 한다.[32]

ALGOL 60 기반 구조

Simula 프로그램의 기본 단위는 ALGOL 60과 마찬가지로 beginend로 둘러싸인 블록이다. 블록은 선언부와 실행문으로 구성되며, 블록 안에서 선언한 이름은 기본적으로 해당 블록과 그 안에 중첩된 블록에서만 사용할 수 있다.

begin
    integer Count;
    real Total, Average;

    Count := 4;
    Total := 42.0;
    Average := Total / Count;

    OutText("Average: ");
    OutFix(Average, 2, 8);
    OutImage
end;

블록이 실행되면 해당 블록에 선언된 변수와 절차를 위한 동적 저장 공간이 생성된다. 일반 블록의 저장 공간은 블록 실행이 종료되면 더 이상 접근할 수 없지만, 클래스 객체의 저장 공간은 객체를 가리키는 참조가 존재하는 동안 유지될 수 있다.

Simula는 ALGOL 60의 블록 구조를 유지하면서 문자열을 나타내는 text, 객체를 가리키는 ref, 클래스 선언, 객체 생성과 참조 비교 등의 기능을 추가했다. 변수의 타입과 선언은 컴파일 시점에 결정되며, 이름의 유효 범위는 블록의 정적 중첩 관계에 따라 정해진다.

블록과 절차

Simula의 절차는 값을 반환하는 형식 절차와 값을 직접 반환하지 않는 일반 절차로 나뉜다. 절차 매개변수는 기본적으로 이름 호출 방식의 영향을 받으며, value를 지정하면 값으로 전달된다. 매개변수의 타입과 전송 방식은 별도의 명세 부분에 작성한다.

real procedure Distance(X1, Y1, X2, Y2);
    value X1, Y1, X2, Y2;
    real X1, Y1, X2, Y2;
begin
    Distance := Sqrt(
        (X2 - X1) ** 2 +
        (Y2 - Y1) ** 2
    )
end;

절차의 반환값은 절차 이름에 값을 대입하여 정한다. 위 코드에서 Distance :=는 지역 변수 대입이 아니라 절차의 결과값을 설정하는 연산이다.

Simula에서는 절차가 클래스 내부에도 선언될 수 있다. 클래스 내부의 절차는 해당 객체의 속성에 직접 접근할 수 있으며, 객체 참조를 통해 외부에서 호출할 수도 있다. 현대 객체 지향 언어에서 메서드라고 부르는 기능이 Simula 문서에서는 주로 클래스에 포함된 procedure attribute로 설명되었다.

class Account(InitialBalance);
    real InitialBalance;
begin
    real Balance;

    procedure Deposit(Amount);
        value Amount;
        real Amount;
    begin
        Balance := Balance + Amount
    end;

    Boolean procedure Withdraw(Amount);
        value Amount;
        real Amount;
    begin
        if Amount <= Balance then
        begin
            Balance := Balance - Amount;
            Withdraw := true
        end
        else
            Withdraw := false
    end;

    Balance := InitialBalance
end;

Balance는 각 Account 객체에 개별적으로 존재한다. DepositWithdraw는 자신이 속한 객체의 Balance를 별도의 객체 인자 없이 읽고 변경한다.

클래스와 객체

클래스는 같은 구조와 동작을 가지는 객체를 생성하기 위한 선언이다. 클래스 선언은 매개변수, 지역 변수, 절차, 내부 클래스와 실행문을 포함할 수 있다.

class Point(X, Y);
    value X, Y;
    real X, Y;
begin
    real procedure DistanceTo(Other);
        ref(Point) Other;
    begin
        DistanceTo := Sqrt(
            (X - Other.X) ** 2 +
            (Y - Other.Y) ** 2
        )
    end
end;

클래스의 매개변수와 내부 선언은 객체의 속성이 된다. Point 객체마다 XY의 값이 따로 존재하며, DistanceTo 절차 역시 각 객체의 속성으로 접근된다.

객체는 new 식을 통해 동적으로 생성한다.

ref(Point) A, B;

A :- new Point(2.0, 3.0);
B :- new Point(8.0, 11.0);

OutFix(A.DistanceTo(B), 2, 10);
OutImage;

new Point(2.0, 3.0)Point 클래스에 해당하는 새 객체를 생성하고 그 객체를 가리키는 참조를 반환한다. 클래스 본문의 실행문은 객체가 생성될 때 실행된다.

Simula의 객체는 단순히 필드와 절차를 담는 수동적인 레코드가 아니다. 클래스 본문은 실행 가능한 명령문을 포함하며, 객체는 자신의 실행 상태를 가질 수 있다. 이 특성 때문에 Simula의 클래스는 데이터 레코드, 블록, 절차와 코루틴의 성질을 결합한 구조로 설명된다.[33]

클래스 본문의 실행

객체가 생성되면 클래스 본문은 일반 블록과 비슷하게 위에서 아래로 실행된다. 선언부 뒤의 실행문은 현대 객체 지향 언어의 생성자 본문에 가까운 역할을 하지만, Simula에서는 객체가 클래스 본문 전체의 실행 상태를 보존할 수도 있다.

class Counter(StartValue);
    value StartValue;
    integer StartValue;
begin
    integer Value;

    procedure Increment;
        Value := Value + 1;

    procedure Reset;
        Value := StartValue;

    Value := StartValue
end;

new Counter(10)을 실행하면 객체의 Value가 10으로 초기화된다. 클래스 본문의 끝에 도달한 객체는 종료된 객체가 되지만, 참조가 남아 있다면 객체의 데이터와 절차 속성에는 계속 접근할 수 있다.

클래스 본문 실행이 detach에 의해 중단되면 객체는 종료되지 않은 상태로 실행 위치를 보존한다. 해당 객체를 다시 활성화하면 중단된 지점 이후부터 실행을 계속할 수 있다.

객체 참조

객체는 ref(클래스명) 타입의 변수로 가리킨다. 참조 변수는 해당 클래스의 객체뿐 아니라 그 클래스에서 파생된 하위 클래스 객체도 가리킬 수 있다.

ref(Point) Position;

Position :- new Point(10.0, 20.0);

OutFix(Position.X, 2, 8);
OutFix(Position.Y, 2, 8);
OutImage;

Simula는 일반 값 대입과 참조 대입을 서로 다른 기호로 구분한다.

Number := 10;
ObjectReference :- new Point(1.0, 2.0);

:=는 정수, 실수, Boolean과 같은 값 또는 텍스트 값을 대입하는 연산이며, :-는 객체의 참조를 복사하는 참조 대입이다. 참조 대입은 객체 자체를 복제하지 않으며, 여러 참조 변수가 같은 객체를 가리킬 수 있다.

ref(Point) First, Second;

First :- new Point(4.0, 5.0);
Second :- First;

Second.X := 100.0;

! First와 Second는 동일한 객체를 가리킨다.;
OutFix(First.X, 2, 8);
OutImage;

참조가 어떤 객체도 가리키지 않는 상태는 none으로 나타낸다.

ref(Point) Current;

Current :- none;

if Current == none then
    OutText("No point selected");

참조의 동일성 비교에는 ===/=를 사용한다. 이는 두 참조가 같은 객체를 가리키는지를 검사하며, 객체가 가지고 있는 데이터의 값이 같은지를 비교하는 연산은 아니다.

ref(Point) A, B, C;

A :- new Point(1.0, 2.0);
B :- A;
C :- new Point(1.0, 2.0);

if A == B then
    OutText("A and B reference the same object");

OutImage;

if A =/= C then
    OutText("A and C are different objects");

OutImage;

AC의 좌표는 같지만 서로 다른 new 식으로 생성되었으므로 별개의 객체다.

원격 식별과 속성 접근

객체의 속성에는 점 표기법을 사용해 접근한다. Simula의 언어 정의에서는 이를 원격 식별 또는 remote identification이라고 부른다.

Position.X := 12.5;
Position.Y := 30.0;
Position.DistanceTo(OtherPosition);

점 왼쪽의 표현식은 객체 참조여야 하며, 오른쪽 이름은 해당 객체가 속한 클래스에서 접근 가능한 속성이어야 한다.

Simula는 점 표기법 외에도 inspect 문을 제공한다. inspect는 객체를 검사하고, 해당 객체의 속성을 한정자 없이 사용할 수 있는 임시 문맥을 만든다.

inspect Position do
begin
    X := X + 10.0;
    Y := Y + 5.0;

    OutFix(X, 2, 8);
    OutFix(Y, 2, 8);
    OutImage
end;

inspect 대상이 none이면 do 뒤의 문장은 실행되지 않는다. otherwise 절을 추가하면 참조가 none이거나 지정한 클래스와 호환되지 않을 때의 동작을 정의할 수 있다.

inspect Position
do
    OutText("A point is available")
otherwise
    OutText("No point is available");

OutImage;

inspectwhen 절과 함께 사용하여 객체의 실제 클래스에 따라 실행 경로를 선택할 수도 있다.

inspect ShapeReference
when Circle do
    OutFix(Radius, 2, 8)
when Rectangle do
begin
    OutFix(Width, 2, 8);
    OutFix(Height, 2, 8)
end
otherwise
    OutText("Unknown shape");

OutImage;

이 구조는 현대 언어의 타입 패턴 매칭이나 하향 형변환을 포함한 분기와 유사하지만, 선택된 객체의 속성을 해당 when 문장 안에서 직접 사용할 수 있다는 특징이 있다.

접두 클래스와 상속

Simula에서 클래스 상속은 prefixing이라는 일반화된 블록 접두 구조로 정의된다. 클래스 선언 앞에 다른 클래스 이름을 붙이면 앞의 클래스가 접두 클래스가 되고, 새 클래스는 접두 클래스의 속성과 실행 구조를 포함한다.

class Vehicle(Name);
    text Name;
begin
    real Position, Speed;

    procedure Move(Duration);
        value Duration;
        real Duration;
    begin
        Position := Position + Speed * Duration
    end
end;

Vehicle class Car(Name, PassengerCapacity);
    text Name;
    integer PassengerCapacity;
begin
    Boolean EngineRunning;

    procedure StartEngine;
        EngineRunning := true;

    procedure StopEngine;
        EngineRunning := false
end;

CarVehicleName, Position, SpeedMove를 포함하고, 여기에 PassengerCapacity, EngineRunning, StartEngineStopEngine을 추가한다.

ref(Car) MyCar;

MyCar :- new Car("Simula", 4);
MyCar.Speed := 20.0;
MyCar.StartEngine;
MyCar.Move(5.0);

OutFix(MyCar.Position, 2, 10);
OutImage;

접두 클래스는 하나만 지정할 수 있으므로 Simula의 클래스 계층은 단일 상속 구조를 가진다. 그러나 클래스 내부에 다른 클래스를 선언할 수 있고, 접두 구조를 여러 단계로 이어서 계층을 만들 수 있다.

class Entity;
begin
    integer Identifier
end;

Entity class MovingEntity;
begin
    real X, Y, VelocityX, VelocityY
end;

MovingEntity class Aircraft;
begin
    real Altitude
end;

Aircraft 객체는 Entity, MovingEntityAircraft에 선언된 모든 접근 가능한 속성을 가진다.

접두 클래스의 객체는 접두 부분과 주 클래스 부분이 따로 생성되는 두 객체가 아니다. 전체 클래스 계층은 하나의 객체를 형성하며, 참조가 상위 클래스 타입으로 선언되어 있어도 같은 객체를 가리킨다.

ref(Entity) AnyEntity;
ref(Aircraft) Plane;

Plane :- new Aircraft;
AnyEntity :- Plane;

AnyEntity를 통해서는 정적 타입인 Entity에 선언된 속성만 직접 접근할 수 있지만, 참조가 가리키는 실제 객체는 여전히 Aircraft 객체다.

qua와 참조 한정

상위 클래스 타입의 참조를 특정 하위 클래스 타입으로 한정하려면 qua 연산자를 사용할 수 있다. qua는 객체가 지정된 클래스에 속하는지 실행 시점에 확인한 뒤, 해당 클래스의 속성에 접근할 수 있는 한정된 참조를 만든다.

ref(Entity) Item;
ref(Aircraft) Plane;

Item :- new Aircraft;
Plane :- Item qua Aircraft;

Plane.Altitude := 1000.0;

객체가 지정된 클래스와 호환되지 않으면 런타임 오류가 발생한다. 따라서 실제 클래스가 불확실한 경우에는 inspectwhen을 사용하는 방식이 더 적합하다.

inspect Item
when Aircraft do
begin
    Altitude := 1000.0;
    OutText("Aircraft altitude updated")
end
otherwise
    OutText("Item is not an aircraft");

OutImage;

가상 절차

가상 절차는 상위 클래스에서 절차의 이름과 형식을 선언하고, 하위 클래스에서 실제 구현을 제공할 수 있도록 하는 기능이다. 가상 속성은 클래스 선언의 virtual 명세부에 기록한다.

class Shape;
virtual:
    procedure Draw;
begin
    procedure Draw;
        OutText("Shape")
end;

Shape class Circle(Radius);
    real Radius;
begin
    procedure Draw;
    begin
        OutText("Circle, radius = ");
        OutFix(Radius, 2, 8);
        OutImage
    end
end;

Shape class Rectangle(Width, Height);
    real Width, Height;
begin
    procedure Draw;
    begin
        OutText("Rectangle, width = ");
        OutFix(Width, 2, 8);
        OutText(", height = ");
        OutFix(Height, 2, 8);
        OutImage
    end
end;

상위 클래스 타입의 참조를 통해 Draw를 호출하더라도, 실행되는 절차는 참조가 실제로 가리키는 객체의 클래스에 따라 정해진다.

ref(Shape) CurrentShape;

CurrentShape :- new Circle(5.0);
CurrentShape.Draw;

CurrentShape :- new Rectangle(10.0, 4.0);
CurrentShape.Draw;

첫 번째 호출은 Circle.Draw를, 두 번째 호출은 Rectangle.Draw를 실행한다. 이는 현대 객체 지향 언어의 동적 디스패치에 해당한다.

Simula의 가상 절차 선언은 단순히 virtual 키워드를 절차 구현 앞에 붙이는 현대 문법과 다르다. 클래스 머리 부분의 virtual: 명세에서 가상 속성을 먼저 선언하고, 클래스 본문이나 하위 클래스에서 그 속성에 대응하는 절차 선언을 제공한다.

가상 속성에는 절차뿐 아니라 label과 switch도 사용할 수 있었다. 그러나 이후 객체 지향 언어에 가장 큰 영향을 준 것은 가상 절차였다.[34]

inner와 클래스 실행 결합

Simula의 상속은 상위 클래스의 데이터와 절차만 복사하는 구조가 아니다. 접두 클래스의 실행문과 하위 클래스의 실행문도 inner 문을 통해 결합된다.

상위 클래스 본문에서 inner를 실행하면, 객체의 실제 하위 클래스에 정의된 본문으로 제어가 이동한다. 하위 클래스 본문이 끝나면 다시 상위 클래스의 inner 다음 문장으로 돌아온다.

class Window;
begin
    OutText("Create native window");
    OutImage;

    inner;

    OutText("Show window");
    OutImage
end;

Window class GameWindow;
begin
    OutText("Create rendering surface");
    OutImage
end;

new GameWindow를 실행하면 개념적으로 다음 순서로 출력된다.

Create native window
Create rendering surface
Show window

상위 클래스의 inner 앞부분은 하위 클래스 본문보다 먼저 실행되고, inner 뒷부분은 하위 클래스 본문이 끝난 뒤 실행된다. 이는 현대 언어의 생성자 호출 순서나 템플릿 메서드 패턴과 비슷한 효과를 언어 실행 모델 자체에서 제공한다.

하위 클래스가 다시 다른 클래스의 접두 클래스가 되면 inner는 클래스 계층을 따라 더 안쪽의 클래스 본문을 실행한다.

class A;
begin
    OutText("A before");
    OutImage;

    inner;

    OutText("A after");
    OutImage
end;

A class B;
begin
    OutText("B before");
    OutImage;

    inner;

    OutText("B after");
    OutImage
end;

B class C;
begin
    OutText("C");
    OutImage
end;

new C의 실행 순서는 다음과 같다.

A before
B before
C
B after
A after

inner는 상위 구현을 하위 구현에서 호출하는 super와 방향이 다르다. super 계열 문법은 하위 클래스가 상위 클래스의 동작을 호출하지만, Simula의 inner는 상위 클래스가 자신의 확장 지점에서 하위 클래스의 동작을 호출한다.

this와 현재 객체

클래스 내부에서는 this 클래스명을 사용해 현재 실행 중인 해당 클래스 객체를 참조할 수 있다.

class Node;
begin
    ref(Node) Next;

    procedure ConnectTo(Other);
        ref(Node) Other;
    begin
        this Node.Next :- Other
    end
end;

this Node는 현재 실행 중인 Node 객체를 나타낸다. 하위 클래스 안에서는 여러 접두 클래스 이름을 사용해 현재 객체의 해당 클래스 수준을 명시할 수 있다.

class Entity;
begin
    procedure PrintIdentity;
    begin
        if this Entity == none then
            OutText("No entity")
        else
            OutText("Entity exists")
    end
end;

현대 언어의 thisself가 별도의 클래스 이름 없이 현재 객체 전체를 나타내는 것과 달리, Simula의 thisthis C 형태로 사용된다. 이는 정적으로 둘러싸인 클래스 활성 객체 가운데 지정한 클래스에 해당하는 객체를 선택하는 구조다.

중첩 클래스

클래스는 다른 클래스나 블록 안에 선언할 수 있다. 중첩 클래스의 객체는 자신을 둘러싼 외부 객체의 속성에 접근할 수 있으며, 이 관계는 객체가 생성된 이후에도 유지된다.

class Graph;
begin
    integer NextIdentifier;

    class Vertex;
    begin
        integer Identifier;

        NextIdentifier := NextIdentifier + 1;
        Identifier := NextIdentifier
    end;

    ref(Vertex) First, Second;

    First :- new Vertex;
    Second :- new Vertex
end;

Vertex 객체는 자신을 생성한 Graph 객체의 NextIdentifier에 접근한다. 서로 다른 Graph 객체에서 생성된 Vertex 객체는 각자 자신을 둘러싼 Graph 객체와 연결된다.

이러한 중첩 구조는 클래스가 단순한 전역 타입 선언이 아니라 특정 객체의 문맥 안에서 정의되고 생성될 수 있음을 의미한다. Simula의 후속 언어인 BETA는 이 중첩과 패턴 개념을 더욱 일반화했다.

객체의 상태

Simula 객체는 실행 상태에 따라 여러 단계로 구분된다.

  • attached: 현재 다른 블록이나 객체에 종속되어 실행 중인 상태
  • detached: 실행 위치를 보존한 채 독립적으로 중단된 상태
  • resumed: 준병렬 시스템에서 실행권을 받은 상태
  • terminated: 클래스 본문의 마지막에 도달해 실행이 끝난 상태

종료된 객체도 참조가 남아 있다면 데이터와 절차 속성은 계속 사용할 수 있다. 다만 클래스 본문의 실행을 다시 이어갈 수는 없다.

객체가 detach를 실행하면 현재 실행 지점을 보존하고 객체 생성자를 호출한 문맥으로 제어를 반환한다.

class Worker(Name);
    text Name;
begin
    while true do
    begin
        OutText(Name);
        OutText(" performs one step");
        OutImage;

        detach
    end
end;

Worker 객체는 한 단계의 작업을 수행한 다음 detach되어 호출자에게 실행권을 돌려준다. 객체가 다시 resume되면 이전 detach 이후의 위치에서 계속 실행한다.

준병렬 실행

Simula는 여러 객체가 하나의 프로세서를 번갈아 사용하면서 각각 독립적인 실행 흐름을 가진 것처럼 동작하는 준병렬 실행을 지원한다. 이는 운영체제 스레드의 선점형 병렬 실행과 달리, 프로그램이 명시적으로 실행권을 넘기는 협력적 실행 모델이다.

기본적인 준병렬 실행은 detach, callresume으로 제어한다.

class Coroutine(Name);
    text Name;
begin
    integer Step;

    for Step := 1 step 1 until 3 do
    begin
        OutText(Name);
        OutText(" step ");
        OutInt(Step, 1);
        OutImage;

        detach
    end
end;

ref(Coroutine) First, Second;

First :- new Coroutine("First");
Second :- new Coroutine("Second");

resume(First);
resume(Second);
resume(First);
resume(Second);

각 객체는 자신의 지역 변수와 실행 위치를 유지한다. 한 객체가 중단되더라도 호출 스택과 반복문의 현재 상태가 사라지지 않으므로, 다음에 활성화될 때 중단된 지점부터 실행된다.

call은 일반적인 코루틴 호출 구조를 만들고, resume은 현재 준병렬 시스템 안에서 실행 중인 객체를 교체한다. 언어 정의는 객체 사이의 동적 연결 관계를 통해 어떤 객체로 제어가 돌아가야 하는지를 규정했다.

이 기능은 시뮬레이션 대상마다 독립적인 행동 순서를 자연스럽게 작성하기 위해 도입되었다. 예를 들어 고객 객체는 도착하고, 줄을 서고, 서비스를 기다리고, 떠나는 전체 생명 주기를 하나의 연속된 코드로 표현할 수 있다. 중앙 사건 처리기가 모든 객체의 상태를 직접 분기하는 구조보다 모델의 실제 행동 순서에 가까운 코드를 작성할 수 있다.[35]

객체 수명과 가비지 컬렉션

Simula의 객체는 new를 통해 동적으로 생성되며 명시적인 객체 해제 연산을 제공하지 않는다. 실행 중인 객체나 다른 참조에서 더 이상 도달할 수 없는 객체는 가비지 컬렉터가 회수할 수 있다.

ref(Point) Current;

Current :- new Point(1.0, 2.0);
Current :- new Point(3.0, 4.0);

두 번째 참조 대입 이후 첫 번째 Point 객체를 가리키는 다른 참조가 없다면 그 객체는 프로그램에서 접근할 수 없게 된다. 런타임은 이러한 객체가 차지한 저장 공간을 자동으로 회수할 수 있다.

객체의 수명은 클래스를 선언한 정적 블록의 실행 여부와 단순히 일치하지 않는다. 클래스 객체는 자신을 생성한 블록의 실행이 끝난 뒤에도 참조를 통해 유지될 수 있으며, 중첩 클래스 객체는 자신이 의존하는 외부 객체의 문맥을 함께 보존한다.

이 구조는 메모리의 물리적 할당 방식보다 객체 사이의 도달 가능성과 언어 수준의 참조 관계를 중심으로 수명을 정의한다.

접근 제어

초기 Simula 67의 클래스 속성은 기본적으로 원격 접근이 가능했다. 이후 표준에서는 클래스 구현의 세부 사항을 감추고 상속 계층의 접근 범위를 제어하기 위해 hiddenprotected 명세가 추가되었다.

hidden으로 지정된 속성은 일반적인 외부 원격 접근에서 숨길 수 있다.

class Account(InitialBalance);
    real InitialBalance;
hidden Balance;
begin
    real Balance;

    procedure Deposit(Amount);
        value Amount;
        real Amount;
    begin
        Balance := Balance + Amount
    end;

    real procedure GetBalance;
        GetBalance := Balance;

    Balance := InitialBalance
end;

외부 코드는 AccountReference.Balance에 직접 접근하지 않고 DepositGetBalance를 통해 상태를 다룬다.

protected는 속성의 재정의나 원격 접근과 관련된 제한을 지정한다. 후기 Simula 표준에서 hiddenprotected는 단순한 가시성 키워드라기보다, 접두 클래스의 속성이 하위 클래스와 외부 문맥에서 어떻게 사용될 수 있는지를 세밀하게 규정하는 명세로 동작했다.

타입 체계

Simula는 변수와 절차 결과의 타입을 선언하는 정적 타입 언어이다. 기본 타입으로는 integer, real, long real, Boolean, charactertext가 있으며, 클래스 이름은 참조 타입을 구성하는 데 사용된다.

integer Count;
real Distance;
Boolean Finished;
character Symbol;
text Message;
ref(Point) Position;

정수는 필요한 경우 실수 문맥에서 사용될 수 있지만, 객체 참조는 선언된 클래스 계층에 따라 호환성이 검사된다. ref(Base) 변수는 Base 또는 그 하위 클래스의 객체를 가리킬 수 있지만, ref(Derived) 변수에 일반적인 ref(Base) 값을 직접 대입할 수는 없다.

ref(Vehicle) VehicleReference;
ref(Car) CarReference;

CarReference :- new Car("Example", 4);
VehicleReference :- CarReference;

! 실제 객체가 Car임을 확인하거나 한정한다.;
CarReference :- VehicleReference qua Car;

클래스 호환성은 클래스 선언의 이름과 접두 계층을 기준으로 결정된다. 구조적으로 같은 속성을 가진 두 클래스라도 같은 클래스 계층에 속하지 않으면 서로 호환되는 참조 타입으로 취급되지 않는다. 따라서 Simula의 객체 타입 체계는 명목적 타입에 해당한다.

텍스트 처리

Simula의 text는 단순한 문자열 값보다는 문자 배열의 구간을 가리키는 기술자에 가깝다. 텍스트 값은 문자 저장 영역과 시작 위치, 길이와 현재 위치 정보를 포함하며, 여러 텍스트 변수가 같은 문자 저장 영역의 서로 다른 구간을 참조할 수 있다.

text Message, Prefix;

Message :- "Simula language";
Prefix :- Message.Sub(1, 6);

OutText(Prefix);
OutImage;

참조형 객체에 사용하는 :-와 비슷하게 텍스트 참조 대입에도 :-가 사용된다. 반면 :=는 오른쪽 텍스트의 문자를 왼쪽 텍스트가 나타내는 프레임에 복사한다.

text Source, Target;

Source :- "SIMULA";
Target :- Blanks(6);

Target := Source;

Target :- Source는 두 텍스트 변수가 같은 텍스트 프레임을 나타내게 하지만, Target := Source는 문자를 복사한다. 이 구분은 객체 참조와 객체 내용의 차이를 명시적으로 표현하려는 Simula의 대입 체계와 연결된다.

시뮬레이션 기능의 분리

Simula 67의 핵심 언어는 범용 객체 지향 언어이며, 이산 사건 시뮬레이션 기능은 SIMSETSIMULATION이라는 표준 시스템 클래스로 분리되었다.

SIMSET은 양방향 연결 목록을 구성하는 head, link와 관련 연산을 제공한다. 시뮬레이션 객체는 link를 상속받아 대기열과 자원 목록에 들어갈 수 있다.

Simset begin
    ref(Head) WaitingLine;
    ref(Link) Item;

    WaitingLine :- new Head;
    Item :- new Link;

    Item.Into(WaitingLine)
end;

SIMULATIONSIMSET을 기반으로 시뮬레이션 시간, 사건 목록과 프로세스 활성화 연산을 제공한다. 시뮬레이션 프로그램은 Simulation을 프로그램 블록의 접두 클래스로 사용한다.

Simulation begin
    process class Customer(Name);
        text Name;
    begin
        OutText(Name);
        OutText(" arrives at ");
        OutFix(Time, 2, 8);
        OutImage;

        Hold(5.0);

        OutText(Name);
        OutText(" leaves at ");
        OutFix(Time, 2, 8);
        OutImage
    end;

    ref(Customer) FirstCustomer;

    FirstCustomer :- new Customer("Customer 1");
    Activate FirstCustomer;
    Hold(10.0)
end;

process는 독립적인 원시 언어 구조가 아니라 Simulation 안에 선언된 클래스다. Time, HoldActivate 역시 시스템 클래스가 제공하는 속성이다. 따라서 시뮬레이션 모델은 핵심 언어를 특별한 문법으로 변경하지 않고 클래스 접두 구조를 통해 구성된다.

이는 Simula 설계에서 중요한 변화였다. Simula I에서는 시뮬레이션 프로세스와 사건 제어가 언어 자체의 중심 기능이었지만, Simula 67에서는 범용 클래스와 객체 구조가 핵심이 되고 시뮬레이션은 해당 구조로 작성된 재사용 가능한 라이브러리 계층이 되었다.[36]

언어 설계의 특징

Simula의 언어 설계는 현실의 대상을 객체로 표현한다는 표면적인 특징보다, ALGOL 블록의 실행 모델을 일반화한 방식에 핵심이 있다. 클래스 객체는 다음 요소를 하나의 단위로 결합한다.

  • 정적으로 정의된 데이터와 절차의 범위
  • 실행할 때 생성되는 동적 저장 공간
  • 다른 객체에서 사용할 수 있는 객체 참조
  • 클래스 본문의 독립적인 실행 상태
  • 접두 클래스에서 이어지는 속성과 실행 구조
  • 가상 절차를 통한 실행 시점의 동작 선택
  • 가비지 컬렉션으로 관리되는 동적 수명

따라서 Simula의 객체는 단순한 데이터 구조도 아니고 독립적인 프로세스만도 아니다. 객체는 데이터, 동작, 실행 문맥, 타입과 생명 주기를 함께 가지는 프로그램 구성 단위다.

Simula의 클래스 계층은 현대의 클래스 기반 언어와 유사하지만 완전히 같지는 않다. 특히 inner를 이용한 실행문 결합, 클래스 본문 자체의 중단과 재개, this 클래스명, 텍스트의 프레임 의미론, 값 대입 :=와 참조 대입 :-의 분리, 클래스 기반 시스템 접두 구조는 Simula 고유의 설계 특성을 보여준다.

이 구조는 시뮬레이션 대상이 자신의 상태와 행동 순서를 직접 소유하도록 만들기 위해 형성되었다. 이후 객체 지향 언어는 Simula의 클래스, 객체 참조, 상속과 가상 절차를 널리 계승했지만, 준병렬 객체와 inner 중심의 클래스 본문 결합은 언어마다 코루틴, 생성자, 메서드 재정의와 프레임워크 구조 등 서로 다른 기능으로 분리되어 발전했다.

시뮬레이션 모델

Simula의 시뮬레이션 모델은 시간에 따라 상태가 연속적으로 변하는 모든 물리 현상을 직접 계산하는 방식보다, 시스템의 상태가 특정 사건이 발생하는 시점에 바뀌는 이산 사건 시뮬레이션에 초점을 둔다. 모델의 사람, 차량, 기계, 주문과 작업 같은 구성 요소는 객체로 표현되고, 그중 시간에 따른 독립적인 행동 순서를 가지는 요소는 Process의 하위 클래스가 된다. 각 프로세스는 자신의 상태와 실행 위치를 유지하면서 사건 목록에 의해 순서대로 실행된다.[37]

Simula 67에서 이러한 기능은 핵심 언어 문법 전체에 고정되어 있지 않다. 범용 객체 지향 언어 위에 SIMSETSIMULATION이라는 시스템 클래스를 제공하고, 시뮬레이션 프로그램이 Simulation을 프로그램 블록의 접두 클래스로 사용하도록 설계되었다. SIMSET은 연결 목록과 대기열을 구성하는 기능을 제공하며, SIMULATION은 그 위에 시뮬레이션 시간, 프로세스, 사건 목록과 스케줄링 연산을 추가한다.[38]

Simulation
begin
    Process class Worker;
    begin
        OutText("Worker starts at ");
        OutFix(Time, 2, 8);
        OutImage;

        Hold(5.0);

        OutText("Worker finishes at ");
        OutFix(Time, 2, 8);
        OutImage
    end;

    ref(Worker) FirstWorker;

    FirstWorker :- new Worker;
    Activate FirstWorker;

    Hold(10.0)
end;

프로그램 블록 앞의 Simulation은 프로그램 전체를 SIMULATION 클래스의 하위 블록으로 만든다. 이에 따라 Process, Time, HoldActivate를 사용할 수 있다. new Worker로 생성된 프로세스 객체는 곧바로 행동을 시작하지 않고 수동 상태로 대기하며, Activate FirstWorker가 실행되어 사건 목록에 들어간 뒤에야 클래스 본문의 실행을 시작한다.[39]

이산 사건 시뮬레이션

이산 사건 시스템에서는 시뮬레이션 시간의 모든 순간을 일정한 간격으로 계산하지 않는다. 상태 변화가 예정된 가장 가까운 사건 시점으로 시간을 이동한 뒤, 그 사건과 연결된 프로세스를 실행한다. 프로세스가 다시 미래의 사건을 예약하거나 다른 프로세스를 활성화하면 사건 목록이 갱신되고, 다음으로 가장 이른 사건이 선택된다.

예를 들어 한 기계가 현재 시각 5에 작업을 시작하고 3시간 뒤에 작업을 끝낸다면, 시각 5와 시각 8 사이의 모든 순간을 반복문으로 계산할 필요가 없다. 프로세스는 Hold(3.0)을 실행해 자신을 시각 8에 다시 실행하도록 예약하고 제어권을 다른 프로세스에 넘긴다.

Process class Machine;
begin
    OutText("Processing starts at ");
    OutFix(Time, 2, 8);
    OutImage;

    Hold(3.0);

    OutText("Processing ends at ");
    OutFix(Time, 2, 8);
    OutImage
end;

Hold(3.0)은 실제 컴퓨터를 3시간 동안 정지시키지 않는다. 현재 프로세스의 예정 사건 시각을 현재 시뮬레이션 시각보다 3만큼 뒤로 이동시키고, 사건 목록에서 더 이른 시각에 예정된 다른 프로세스가 실행되도록 한다. 실행할 다른 사건이 없다면 시뮬레이션 시각은 곧바로 3만큼 전진한다.[40]

프로세스 상호작용 방식

Simula는 이산 사건 모델을 작성하는 여러 방식 가운데 프로세스 상호작용 방식을 언어 구조와 직접 결합했다. 모델 작성자는 중앙 사건 처리기 안에서 모든 사건 종류를 구분하는 대신, 각 대상이 시간의 흐름에 따라 수행하는 행동을 하나의 연속된 프로세스 본문으로 작성한다.

고객 프로세스라면 도착, 대기, 서비스와 퇴장을 고객 클래스 안에 순서대로 표현할 수 있다.

Process class Customer(Name);
    text Name;
begin
    OutText(Name);
    OutText(" arrives at ");
    OutFix(Time, 2, 8);
    OutImage;

    Wait(ServiceQueue);

    OutText(Name);
    OutText(" begins service at ");
    OutFix(Time, 2, 8);
    OutImage;

    Hold(4.0);

    OutText(Name);
    OutText(" leaves at ");
    OutFix(Time, 2, 8);
    OutImage
end;

이 코드에서 고객의 행동은 현실에서 관찰되는 순서와 비슷하게 읽힌다. 고객은 도착한 뒤 대기열에 들어가고, 외부에서 다시 활성화되면 서비스를 시작하며, 일정 시간 뒤 퇴장한다. 실행이 WaitHold에서 여러 차례 중단되더라도 지역 변수와 현재 실행 위치는 해당 객체 안에 보존된다.

이 모델은 사건 중심 방식과 다른 관점을 제공한다. 사건 중심 프로그램은 ‘고객 도착 사건’, ‘서비스 시작 사건’, ‘서비스 종료 사건’을 별도의 처리 절차로 나누고 고객 상태를 외부 자료 구조에 저장하는 경우가 많다. Simula에서는 하나의 고객 객체가 자신의 상태와 전체 생명 주기를 함께 소유한다. 이러한 구조는 Simula의 객체와 코루틴 개념이 시뮬레이션 문제에서 결합된 결과다.[41]

SIMULATION 시스템 클래스

SIMULATIONSIMSET의 하위 클래스이며, 내부에 Process 클래스를 선언한다. 따라서 모든 프로세스는 연결 목록에 들어갈 수 있는 Link의 속성을 간접적으로 물려받는다. 이 구조를 통해 프로세스 객체는 사건 목록뿐 아니라 서비스 대기열과 자원 대기 목록에도 연결될 수 있다.

Simset
└── Simulation
    └── Process

위 계층은 개념적 관계를 나타낸다. SimulationSimset을 접두 클래스로 사용하고, ProcessSimulation 내부에 선언된 Link의 하위 클래스다. Process 객체는 실행 상태를 저장하는 코루틴이면서 목록에 연결할 수 있는 객체이기도 하다.[42]

시뮬레이션 모델을 구성하는 핵심 속성은 다음과 같다.

요소역할
Time현재 시뮬레이션 시각을 반환한다.
Process독립적인 행동 순서를 가지는 시뮬레이션 객체의 기반 클래스다.
사건 목록실행 예정 프로세스를 사건 시각 순서로 보관한다.
Current현재 실행 중인 프로세스를 반환한다.
Hold현재 프로세스를 일정 시간 뒤로 예약한다.
Activate프로세스를 사건 목록에 넣어 실행을 예약한다.
Reactivate이미 예약된 프로세스도 새로운 위치로 다시 예약한다.
Passivate현재 프로세스를 사건 목록에서 제거한다.
Wait현재 프로세스를 사건 목록에서 제거하고 대기열에 넣는다.
Cancel지정한 프로세스를 사건 목록에서 제거한다.

시뮬레이션 시간

Time은 현재 시뮬레이션 시각을 반환하는 long real 형식의 절차다. 이 값은 벽시계 시간이나 프로그램이 실제로 실행된 시간과 무관하다. 모델 작성자가 초, 분, 시간이나 일 가운데 어떤 단위를 사용하는지에 따라 Time의 의미가 정해진다.

Process class DeliveryTruck;
begin
    ! 이 모델에서 시간 단위는 분이다.;
    Hold(30.0);

    OutText("Truck arrives after ");
    OutFix(Time, 1, 8);
    OutText(" minutes");
    OutImage
end;

시뮬레이션 시작 시각은 일반적으로 0이다. 현재 프로세스가 미래 시각으로 이동하면 런타임은 사건 목록의 첫 항목이 가진 사건 시각을 새로운 현재 시각으로 사용한다. 같은 시각에 여러 프로세스가 예약될 수 있으므로 사건 목록은 시각뿐 아니라 같은 시각 안에서의 순서도 보존한다.[43]

시간은 원칙적으로 뒤로 이동하지 않는다. 과거 시각을 지정하는 활성화 표현은 표준이 규정한 방식에 따라 현재 시각으로 조정되거나 허용되는 범위 안에서 처리된다. 모델은 사건 목록에 있는 가장 이른 사건을 계속 선택하므로 전체 실행 시각은 단조롭게 증가한다.

사건 목록

SIMULATION은 실행 예정 프로세스들을 sequencing set이라 부르는 사건 목록에 보관한다. 개념적으로 각 프로세스에는 사건 통지 객체인 EventNotice가 연결되고, 사건 통지는 해당 프로세스가 다시 실행될 시각인 EvTime을 가진다.

사건 목록은 EvTime이 작은 순서로 정렬된다. 목록의 첫 번째 사건에 연결된 프로세스가 현재 활성 프로세스이며, 그 사건의 EvTime이 현재 Time이 된다.[44]

현재 시각 5.0

프로세스          사건 시각
Worker            5.0
Machine           7.0
Customer          9.5
Main              100.0

WorkerHold(4.0)을 실행하면 사건 시각이 9.0으로 바뀌고 목록의 새 위치로 이동한다.

현재 시각 5.0

프로세스          사건 시각
Machine           7.0
Worker            9.0
Customer          9.5
Main              100.0

실행권은 목록의 새 첫 항목인 Machine으로 넘어가고, 시뮬레이션 시각은 7.0으로 전진한다. 이 사건 목록은 개념적으로 SIMSET 목록으로 설명되지만, 실제 구현은 같은 순서 의미를 유지하는 트리나 다른 우선순위 자료 구조를 사용할 수 있다.

프로세스의 상태

Process 객체는 사건 목록에서의 위치와 클래스 본문의 실행 완료 여부에 따라 상태가 나뉜다.

상태의미
활성사건 목록의 첫 번째 항목이며 현재 실행 중이다.
대기 또는 중단사건 목록에는 있지만 첫 번째 항목이 아니다.
수동사건 목록에 없고 클래스 본문은 아직 끝나지 않았다.
종료클래스 본문의 마지막 end에 도달했다.
유휴사건 목록에 없으며, 수동 또는 종료 상태를 함께 가리킨다.

새로운 Process 객체는 생성 직후 수동 상태가 된다. new는 객체와 실행 문맥을 생성하지만 사용자 정의 프로세스 본문을 즉시 진행시키지는 않는다. 객체가 Activate 등에 의해 사건 목록에 들어가고 첫 번째 위치에 도달해야 본문 실행이 시작된다.[45]

ref(Customer) C;

C :- new Customer("Customer 1");

! C는 아직 수동 상태다.;
Activate C;

! C가 사건 목록에 들어간다.;

Idle은 프로세스가 사건 목록에 없을 때 참을 반환한다. Terminated는 프로세스가 클래스 본문 끝에 도달했는지를 반환한다.

if C.Idle then
begin
    if C.Terminated then
        OutText("The process has terminated")
    else
        OutText("The process is passive")
end;

수동 상태는 C.Idle and not C.Terminated로 판별할 수 있다. 종료된 프로세스는 다시 예약할 수 없지만, 수동 프로세스는 ActivateReactivate로 다시 실행할 수 있다.

EvTime은 해당 프로세스가 활성화될 예정 시각을 반환한다. 프로세스가 유휴 상태이면 예정 사건이 없으므로 EvTime을 호출할 수 없다. NextEv는 사건 목록에서 해당 프로세스 바로 다음에 있는 프로세스를 반환한다.

Hold

Hold(T)는 현재 프로세스의 다음 실행을 현재 시각에서 T만큼 뒤로 미룬다.

Process class Server;
begin
    while true do
    begin
        OutText("Service begins at ");
        OutFix(Time, 2, 8);
        OutImage;

        Hold(2.5);

        OutText("Service ends at ");
        OutFix(Time, 2, 8);
        OutImage;

        Passivate
    end
end;

현재 사건 통지의 EvTimeT가 더해지고, 사건 목록은 시간 순서에 맞게 재배열된다. 다른 프로세스의 사건 시각이 더 이르면 해당 프로세스가 실행된다. T가 0이면 현재 시각은 변하지 않지만, 같은 시각의 다른 사건 뒤로 이동할 수 있으므로 실행권을 양보하는 효과가 생길 수 있다.

Hold는 서비스 시간, 이동 시간, 고장 간격, 작업 지연처럼 프로세스가 일정한 시뮬레이션 시간 동안 별도의 행동을 하지 않는 구간을 표현하는 데 사용된다.

Activate

Activate는 수동 프로세스를 사건 목록에 삽입한다. 가장 단순한 형태는 현재 시각에 프로세스를 활성화한다.

Activate CustomerProcess;

이 형태는 대상 프로세스의 사건 시각을 현재 Time으로 설정하고 현재 프로세스 앞에 삽입한다. 따라서 새로 활성화된 프로세스가 즉시 실행권을 받을 수 있다.

상대적인 지연을 지정할 수도 있다.

Activate CustomerProcess delay 5.0;

이 문장은 대상 프로세스를 Time + 5.0에 실행하도록 예약한다. 같은 시각에 다른 사건이 이미 있다면 기본적으로 그 사건들 뒤에 놓인다.

Activate CustomerProcess delay 5.0 prior;

prior를 사용하면 같은 사건 시각을 가진 기존 프로세스들보다 앞에 삽입한다.

절대 시각도 지정할 수 있다.

Activate CustomerProcess at 20.0;
Activate EmergencyProcess at 20.0 prior;

다른 프로세스를 기준으로 같은 시각의 앞이나 뒤에 배치할 수도 있다.

Activate UrgentOrder before NormalOrder;
Activate Cleanup after ServiceProcess;

beforeafter는 기준 프로세스와 같은 사건 시각을 사용하면서 사건 목록에서의 상대 순서를 지정한다.[46]

ActivateReactivate

Activate는 원칙적으로 수동 프로세스를 예약하는 연산이다. 대상 프로세스가 이미 사건 목록에 있으면 일반적인 Activate는 기존 예약을 함부로 변경하지 않는다. 이미 예약된 프로세스의 사건 시각이나 위치까지 바꾸려면 Reactivate를 사용한다.

Reactivate AlarmProcess at Time + 1.0 prior;

이 코드는 AlarmProcess가 기존에 미래 시각으로 예약되어 있더라도 기존 사건을 제거하고 현재 시각에서 1 뒤에 다시 예약한다.

Reactivate는 예정된 사건의 우선순위를 변경하거나 기존 일정을 취소하고 새 시각으로 옮길 때 사용한다. 예를 들어 기계의 고장 예정 시각을 앞당기거나, 긴급 작업이 발생해 기존 작업 프로세스를 즉시 깨우는 모델을 구성할 수 있다.

if Emergency then
    Reactivate MaintenanceProcess at Time prior;

Passivate

Passivate는 현재 프로세스를 사건 목록에서 제거하고 수동 상태로 만든다.

Process class Machine;
begin
    while true do
    begin
        Passivate;

        OutText("Machine starts at ");
        OutFix(Time, 2, 8);
        OutImage;

        Hold(4.0)
    end
end;

이 기계 프로세스는 외부 객체가 활성화할 때까지 기다린다. Passivate된 프로세스는 자신을 다시 활성화할 수 없으므로, 다른 프로세스가 해당 객체의 참조를 가지고 Activate 또는 Reactivate를 실행해야 한다.

Activate MachineProcess;

Passivate는 다음 사건 시각을 미리 결정할 수 없는 대기에 적합하다. 어떤 자원이 제공되거나 외부 조건이 충족되는 시점에 다른 프로세스가 깨워 주어야 하는 상황을 표현한다.

Cancel

Cancel(P)은 지정한 프로세스를 사건 목록에서 제거해 수동 상태로 만든다.

Cancel(PlannedFailure);

Passivate가 현재 실행 중인 프로세스 자신을 중단하는 데 사용되는 것과 달리, Cancel은 다른 프로세스의 예정 사건을 취소할 수 있다. 기계가 수리되어 기존 고장 사건이 무효가 되거나, 주문이 취소되어 배송 프로세스를 더 이상 실행할 필요가 없는 경우에 사용할 수 있다.

if OrderCancelled then
    Cancel(DeliveryProcess);

Cancel은 프로세스 객체를 삭제하거나 종료시키지 않는다. 클래스 본문 실행이 끝나지 않았다면 대상은 수동 상태가 되므로 나중에 다시 활성화할 수 있다.

대기열과 Wait

Wait(Q)는 현재 프로세스를 사건 목록에서 제거하고 Head 객체가 나타내는 대기열의 마지막에 넣는다.

ref(Head) WaitingQueue;

WaitingQueue :- new Head;

Process class Customer;
begin
    Wait(WaitingQueue);

    OutText("Service starts at ");
    OutFix(Time, 2, 8);
    OutImage
end;

Wait를 호출한 프로세스는 수동 상태가 되며 스스로 대기열을 빠져나와 실행을 계속하지 못한다. 서버나 자원 관리 객체가 대기열에서 프로세스를 제거하고 다시 활성화해야 한다.

ref(Process) NextCustomer;

if not WaitingQueue.Empty then
begin
    NextCustomer :- WaitingQueue.First qua Process;
    NextCustomer.Out;
    Activate NextCustomer
end;

WaitingQueue.First는 대기열의 첫 번째 객체를 반환한다. Out은 해당 객체를 SIMSET 목록에서 제거하고, Activate는 프로세스를 사건 목록으로 되돌린다.

Wait는 단순한 시간 지연이 아니다. Hold는 지정한 시각이 되면 프로세스가 자동으로 다시 실행되지만, Wait는 다른 객체가 명시적으로 깨울 때까지 계속 대기한다. 따라서 서비스 창구, 생산 설비, 통신 채널과 같은 제한된 자원을 기다리는 상황을 표현하는 데 사용된다.[47]

자원과 대기 행렬

Simula의 표준 SIMULATION 클래스는 특정한 ‘서버’나 ‘자원’ 클래스를 고정된 형태로 제공하지 않는다. 모델 작성자는 일반 클래스와 Head 대기열, Process를 조합해 문제에 맞는 자원을 정의한다.

다음은 하나의 서비스 창구를 나타내는 클래스다.

class ServiceDesk;
begin
    ref(Head) Queue;
    Boolean Busy;

    procedure Request;
    begin
        if Busy then
            Wait(Queue);

        Busy := true
    end;

    procedure Release;
    begin
        ref(Process) NextCustomer;

        if Queue.Empty then
            Busy := false
        else
        begin
            NextCustomer :- Queue.First qua Process;
            NextCustomer.Out;
            Activate NextCustomer
        end
    end;

    Queue :- new Head
end;

고객 프로세스는 창구를 요청하고, 서비스를 받은 뒤 자원을 반환한다.

Process class Customer(Name);
    text Name;
begin
    OutText(Name & " arrives at ");
    OutFix(Time, 2, 8);
    OutImage;

    Desk.Request;

    OutText(Name & " starts service at ");
    OutFix(Time, 2, 8);
    OutImage;

    Hold(3.0);

    OutText(Name & " leaves at ");
    OutFix(Time, 2, 8);
    OutImage;

    Desk.Release
end;

창구가 사용 중이면 Request 내부의 Wait가 현재 고객 프로세스를 대기열로 이동시킨다. 기존 고객이 Release를 실행하면 대기열의 첫 고객이 제거되고 활성화된다. 이때 고객 클래스 본문의 실행은 Wait 바로 다음 문장부터 이어진다.

프로세스 생성과 도착 분포

실제 시뮬레이션에서는 고객이나 작업이 일정한 간격보다 확률 분포에 따라 도착하는 경우가 많다. Simula 환경은 균등 분포, 정규 분포, 지수 분포, 포아송 분포와 이산 분포 등을 위한 난수 절차를 제공했다.

다음 생성기 프로세스는 평균 도착 간격을 기준으로 고객을 계속 생성한다.

Process class CustomerGenerator;
begin
    integer Seed, Number;

    Seed := 12345;

    while Time < 100.0 do
    begin
        Number := Number + 1;

        Activate new Customer(
            Copy("Customer " & IntAsText(Number))
        );

        Hold(NegExp(5.0, Seed))
    end
end;

NegExp(5.0, Seed)는 평균이 5인 음의 지수 분포에서 다음 도착 간격을 얻는 용도로 사용할 수 있다. 생성기는 고객을 현재 시각에 활성화한 뒤 다음 도착 시각까지 자신을 보류한다.

난수 스트림의 상태는 정수 매개변수로 전달되고 호출 과정에서 갱신된다. 같은 초기값과 동일한 호출 순서를 사용하면 같은 난수열을 재현할 수 있으므로 모델 실행을 반복하고 결과를 비교하는 데 유용하다.[48]

통계 수집

시뮬레이션의 목적은 객체의 행동을 재현하는 데 그치지 않고 대기 시간, 자원 사용률, 처리량과 대기열 길이 같은 측정값을 얻는 데 있다. Simula에서는 일반 변수와 배열, 클래스 및 표준 통계 절차를 이용해 결과를 수집한다.

long real ArrivalTime, TotalWaitingTime;
integer ServedCustomers;

ArrivalTime := Time;

Desk.Request;

TotalWaitingTime :=
    TotalWaitingTime + (Time - ArrivalTime);

ServedCustomers := ServedCustomers + 1;

고객이 도착할 때 현재 시각을 저장하고, 자원을 얻은 뒤 두 시각의 차이를 누적하면 전체 대기 시간을 구할 수 있다.

if ServedCustomers > 0 then
begin
    OutText("Average waiting time: ");
    OutFix(
        TotalWaitingTime / ServedCustomers,
        2,
        10
    );
    OutImage
end;

시간에 따라 변하는 대기열 길이의 평균을 구하려면 값이 유지된 시간까지 고려해야 한다. 대기열 길이가 변할 때마다 이전 길이와 경과 시간을 곱해 누적하는 방식으로 시간 가중 평균을 계산할 수 있다.

procedure UpdateQueueArea(NewLength);
    value NewLength;
    integer NewLength;
begin
    QueueArea :=
        QueueArea +
        CurrentQueueLength * (Time - LastQueueChange);

    CurrentQueueLength := NewLength;
    LastQueueChange := Time
end;

시뮬레이션이 끝났을 때 QueueArea / Time을 계산하면 관찰 기간 동안의 평균 대기열 길이를 구할 수 있다.

같은 시각의 사건 순서

이산 사건 모델에서는 여러 사건이 같은 시뮬레이션 시각에 발생할 수 있다. Simula의 사건 목록은 같은 EvTime을 가진 사건 사이의 순서를 보존하며, prior, beforeafter를 사용해 순서를 명시적으로 조정할 수 있다.

Activate NormalProcess at 10.0;
Activate UrgentProcess at 10.0 prior;

두 프로세스의 사건 시각은 모두 10이지만 UrgentProcess가 먼저 실행된다.

Activate PrepareProcess before ExecuteProcess;
Activate CleanupProcess after ExecuteProcess;

같은 시각의 실행 순서는 모델 결과에 영향을 줄 수 있다. 예를 들어 시각 10에 자원이 반환되는 사건과 새로운 고객이 자원을 요청하는 사건이 함께 발생하면 어느 사건을 먼저 처리하느냐에 따라 순간적인 대기열 상태가 달라질 수 있다. 따라서 모델 작성자는 동시 사건의 의미를 정하고 필요한 경우 우선순위를 명시해야 한다.

완전한 대기열 모델

다음 프로그램은 고객 생성기, 하나의 서비스 창구, 대기열과 대기 시간 통계를 결합한 예제다.

Simulation
begin
    ref(ServiceDesk) Desk;
    integer Seed, CreatedCustomers, ServedCustomers;
    long real TotalWaitingTime;

    class ServiceDesk;
    begin
        ref(Head) Queue;
        Boolean Busy;

        procedure Request;
        begin
            if Busy then
                Wait(Queue);

            Busy := true
        end;

        procedure Release;
        begin
            ref(Process) NextCustomer;

            if Queue.Empty then
                Busy := false
            else
            begin
                NextCustomer :-
                    Queue.First qua Process;

                NextCustomer.Out;
                Activate NextCustomer
            end
        end;

        Queue :- new Head
    end;

    Process class Customer(Number);
        value Number;
        integer Number;
    begin
        long real ArrivalTime;

        ArrivalTime := Time;

        OutText("Customer ");
        OutInt(Number, 3);
        OutText(" arrives at ");
        OutFix(Time, 2, 8);
        OutImage;

        Desk.Request;

        TotalWaitingTime :=
            TotalWaitingTime +
            (Time - ArrivalTime);

        OutText("Customer ");
        OutInt(Number, 3);
        OutText(" starts service at ");
        OutFix(Time, 2, 8);
        OutImage;

        Hold(3.0);

        ServedCustomers := ServedCustomers + 1;

        OutText("Customer ");
        OutInt(Number, 3);
        OutText(" leaves at ");
        OutFix(Time, 2, 8);
        OutImage;

        Desk.Release
    end;

    Process class Generator;
    begin
        while Time < 60.0 do
        begin
            CreatedCustomers :=
                CreatedCustomers + 1;

            Activate new Customer(
                CreatedCustomers
            );

            Hold(NegExp(5.0, Seed))
        end
    end;

    Seed := 12345;
    Desk :- new ServiceDesk;

    Activate new Generator;

    ! 모델의 관찰 시간을 100으로 제한한다.;
    Hold(100.0);

    OutText("Created customers: ");
    OutInt(CreatedCustomers, 4);
    OutImage;

    OutText("Served customers: ");
    OutInt(ServedCustomers, 4);
    OutImage;

    if ServedCustomers > 0 then
    begin
        OutText("Average waiting time: ");
        OutFix(
            TotalWaitingTime / ServedCustomers,
            2,
            10
        );
        OutImage
    end
end;

프로그램의 주 블록 역시 사건 목록에 참여하는 실행 문맥으로 취급된다. 마지막의 Hold(100.0)은 주 프로그램을 시각 100에 다시 실행하도록 예약하고, 그동안 생성기와 고객 프로세스가 실행되게 한다. 시각 100이 되면 주 블록이 다시 활성화되어 통계를 출력하고 프로그램을 종료한다.

생성기는 시각 60까지만 고객을 생성하지만, 앞서 생성된 고객은 시각 60 이후에도 대기하거나 서비스를 받을 수 있다. 전체 모델은 시각 100에 종료되므로 그 시점까지 서비스를 끝내지 못한 고객은 완료 통계에 포함되지 않는다.

시뮬레이션 종료

Simula 시뮬레이션은 사건 목록에 실행 가능한 프로세스가 남아 있는 동안 계속 진행될 수 있다. 반복적으로 자신이나 다른 프로세스를 미래에 예약하는 모델은 명시적인 종료 조건이 없으면 사실상 무한히 실행될 수 있다.

일반적인 종료 방식은 주 블록을 지정된 시각까지 Hold하는 것이다.

Activate new Generator;
Hold(1000.0);

OutText("Simulation finished");
OutImage;

사건 수나 처리량을 기준으로 종료할 수도 있다.

while ServedCustomers < 1000 do
    Hold(1.0);

또는 주 블록을 Passivate해 두고, 종료 조건을 감지한 관리 프로세스가 주 블록을 다시 활성화하도록 구성할 수 있다.

ref(Process) MainProgram;

MainProgram :- Current;

Activate new Controller;
Passivate;

OutText("Simulation completed");
OutImage;

이 경우 Controller는 조건이 충족되었을 때 Activate MainProgram을 실행한다.

실제 시간과 시뮬레이션 시간의 분리

Simula의 사건 실행은 일반적으로 가능한 한 빠르게 진행된다. 한 시간짜리 공장 운영을 시뮬레이션하는 데 실제로 한 시간이 걸릴 필요는 없으며, 사건 수와 계산량이 적다면 짧은 실제 시간 안에 수년의 모델 시간을 처리할 수도 있다.

반대로 각 사건에서 복잡한 계산을 수행하거나 매우 많은 프로세스를 생성하면 짧은 시뮬레이션 구간도 오랜 실제 실행 시간이 필요할 수 있다. Time은 모델 내부의 논리적 시간일 뿐이며 성능 측정을 위한 실제 경과 시간과 구분된다.

이 분리는 동일한 모델을 여러 번 실행해 확률 분포의 영향을 분석하거나, 매개변수를 변경하면서 수년 동안의 운영 결과를 비교하는 데 필수적이다.

모델 구성의 특징

Simula의 시뮬레이션 모델은 다음 세 구조를 하나로 결합한다.

  • 객체 모델은 시스템의 구성 요소와 상태를 표현한다.
  • 프로세스 모델은 각 구성 요소의 시간에 따른 행동 순서를 표현한다.
  • 사건 목록은 여러 프로세스 사이에서 시뮬레이션 시간을 전진시키고 실행 순서를 결정한다.

객체는 자신의 데이터를 보관하고, 프로세스 본문은 객체의 전체 생명 주기를 기술하며, SIMULATION 런타임은 중단된 프로세스를 사건 시각에 맞추어 다시 실행한다. 이 때문에 모델의 구조와 프로그램의 구조를 비교적 직접적으로 대응시킬 수 있다.

시뮬레이션 대상이 반드시 모두 Process일 필요는 없다. 독립적인 실행 흐름이 필요하지 않은 자원, 통계 수집기, 설정과 수동적 데이터 객체는 일반 클래스로 정의할 수 있다. 시간에 따라 스스로 행동하거나 중단과 재개가 필요한 대상만 Process로 모델링하는 방식이 자연스럽다.

Simula의 프로세스 상호작용 모델은 이후 여러 시뮬레이션 언어와 라이브러리에 계승되었다. 객체나 프로세스가 hold 또는 이에 대응하는 연산으로 일정 시간 대기하고, 자원을 기다리며 실행을 양보한 뒤 이전 위치에서 다시 시작한다는 구조는 현대의 프로세스 기반 이산 사건 시뮬레이션에서도 사용된다. Simula에서는 이 구조가 별도의 외부 실행기가 아니라 클래스, 코루틴, 객체 참조와 연결 목록으로 구성된 언어 환경 안에서 처음 체계적으로 결합되었다.[49]

객체 지향 프로그래밍

Simula 67은 일반적으로 최초의 객체 지향 프로그래밍 언어로 평가된다. Simula 이전에도 자료를 묶는 레코드, 동적 메모리 할당, 절차의 간접 호출과 독립적인 실행 흐름 같은 개별 개념은 존재했지만, Simula 67은 클래스, 객체, 객체 참조, 하위 클래스, 상속, 가상 절차와 동적 객체 생성을 하나의 일관된 타입 및 실행 모델로 결합했다.[50]

Simula의 객체 지향 구조는 범용 소프트웨어를 계층적으로 조직하려는 추상적인 이론에서 먼저 출발하지 않았다. 달과 뉘고르는 공장, 통신망, 교통 체계와 같은 복잡한 시스템을 시뮬레이션하면서, 현실의 각 구성 요소를 프로그램 안에서도 독립된 상태와 행동을 가진 단위로 표현하려 했다. 자동차는 위치와 속도를 보관하고 이동하며, 고객은 도착하고 기다린 뒤 서비스를 받고, 기계는 작업과 고장을 반복하는 식으로 대상마다 자신의 생명 주기를 갖게 했다.[51]

이러한 대상은 처음에는 Simula I의 프로세스로 표현되었다. Simula 67에서는 프로세스가 특정한 시뮬레이션 요소에서 범용적인 클래스와 객체로 일반화되었다. 클래스는 같은 종류의 객체가 공유하는 데이터와 절차의 패턴을 정의하고, 객체는 실행 중 해당 패턴으로부터 생성되는 개별 인스턴스가 되었다. 시뮬레이션 기능은 일반 객체 모델 위에 구현된 SIMULATION 시스템 클래스로 분리되었다.

객체 모델의 형성

Simula의 객체 모델은 ALGOL 60 블록을 확장하는 과정에서 형성되었다. ALGOL 60의 블록은 지역 변수와 절차를 하나의 정적 범위 안에 묶고, 블록이 실행될 때마다 대응하는 동적 저장 영역을 생성한다. 달과 뉘고르는 이 블록을 이름이 있는 생성 패턴으로 정의하고, 실행 중 여러 인스턴스를 만들며, 블록 실행이 끝난 뒤에도 참조를 통해 접근할 수 있도록 확장했다.

일반 ALGOL 블록은 실행될 때 한 번 생성되고 실행이 끝나면 접근할 수 없게 된다.

begin
    integer Value;

    procedure Increment;
        Value := Value + 1;

    Value := 10;
    Increment
end;

Simula의 클래스는 이 블록을 반복적으로 생성할 수 있는 선언으로 바꾼다.

class Counter(Start);
    value Start;
    integer Start;
begin
    integer Value;

    procedure Increment;
        Value := Value + 1;

    Value := Start
end;

Counter 선언 자체는 하나의 객체가 아니다. new Counter(...)가 실행될 때마다 독립된 데이터와 실행 문맥을 가진 객체가 생성된다.

ref(Counter) First, Second;

First :- new Counter(10);
Second :- new Counter(100);

First.Increment;
First.Increment;
Second.Increment;

OutInt(First.Value, 4);
OutImage;

OutInt(Second.Value, 4);
OutImage;

FirstSecond는 같은 클래스에서 생성되었지만 서로 다른 Value를 가진다. 첫 번째 객체의 상태를 변경해도 두 번째 객체의 상태는 바뀌지 않는다.

Simula에서 객체는 클래스의 데이터 저장 영역만을 의미하지 않는다. 객체에는 클래스에 선언된 절차, 해당 객체가 생성된 정적 문맥과 필요한 경우 중단된 실행 위치도 연결된다. 따라서 Simula 객체는 레코드, 모듈과 코루틴의 성질을 함께 가진 실행 단위다.[52]

클래스와 인스턴스

클래스는 객체가 가질 속성과 초기 실행을 정의한다. 클래스의 매개변수, 변수, 배열, 절차, 가상 속성과 내부 클래스는 모두 객체의 속성이 될 수 있다.

class Person(Name, BirthYear);
    text Name;
    integer BirthYear;
begin
    integer procedure AgeAt(Year);
        value Year;
        integer Year;
    begin
        AgeAt := Year - BirthYear
    end
end;

객체는 new 식으로 동적으로 생성된다.

ref(Person) Ada;

Ada :- new Person("Ada", 1815);

OutText(Ada.Name);
OutText(" would be ");
OutInt(Ada.AgeAt(1850), 3);
OutImage;

new Person("Ada", 1815)은 다음 과정을 수행한다.

  1. Person 객체에 필요한 저장 공간을 할당한다.
  2. 클래스 매개변수에 인자를 연결한다.
  3. 객체의 지역 변수와 절차 환경을 구성한다.
  4. 클래스 본문의 실행문을 실행한다.
  5. 생성된 객체를 가리키는 참조를 반환한다.

클래스 본문 끝에 도달한 객체는 실행이 종료된 객체가 되지만, 참조가 남아 있다면 데이터와 절차 속성에는 계속 접근할 수 있다. 독립적인 실행 흐름을 가지는 객체는 detach 등을 통해 실행 위치를 보존한 채 중단될 수도 있다.

객체의 정체성과 참조

Simula에서 객체는 자신의 속성 값과 별개인 정체성을 가진다. 두 객체가 동일한 클래스에서 생성되고 모든 속성 값이 같아도 서로 다른 new 식으로 생성되었다면 별개의 객체다.

ref(Person) First, Second, Alias;

First :- new Person("Kim", 2000);
Second :- new Person("Kim", 2000);
Alias :- First;

if First == Alias then
begin
    OutText("First and Alias are the same object");
    OutImage
end;

if First =/= Second then
begin
    OutText("First and Second are different objects");
    OutImage
end;

FirstAlias는 같은 객체를 가리키므로 == 비교가 참이다. FirstSecond는 속성 값이 같더라도 서로 다른 객체이므로 =/=가 참이다.

객체를 사용하는 변수에는 객체의 내용 자체가 아니라 참조가 저장된다.

ref(Person) Original, OtherName;

Original :- new Person("Dahl", 1931);
OtherName :- Original;

OtherName.BirthYear := 1930;

OutInt(Original.BirthYear, 4);
OutImage;

두 참조는 같은 객체를 가리키므로 OtherName을 통한 변경은 Original을 통해서도 관찰된다. 이러한 별칭 관계는 객체가 값처럼 통째로 복사되지 않고, 여러 객체와 자료 구조에서 공유될 수 있게 한다.

아무 객체도 가리키지 않는 참조는 none으로 표현한다.

ref(Person) Selected;

Selected :- none;

if Selected == none then
begin
    OutText("No person selected");
    OutImage
end;

상태와 동작의 결합

Simula의 클래스는 객체의 상태를 나타내는 변수와 그 상태를 사용하는 절차를 같은 선언 안에 배치한다. 현대 객체 지향 용어로는 데이터와 메서드를 하나의 클래스에 캡슐화하는 구조다.

class BankAccount(InitialBalance);
    value InitialBalance;
    real InitialBalance;
begin
    real Balance;

    procedure Deposit(Amount);
        value Amount;
        real Amount;
    begin
        if Amount > 0.0 then
            Balance := Balance + Amount
    end;

    Boolean procedure Withdraw(Amount);
        value Amount;
        real Amount;
    begin
        if Amount <= Balance then
        begin
            Balance := Balance - Amount;
            Withdraw := true
        end
        else
            Withdraw := false
    end;

    Balance := InitialBalance
end;

DepositWithdraw는 자신이 속한 객체의 Balance를 직접 사용한다. 호출자가 계좌 상태를 별도 인자로 전달할 필요가 없으며, 각 계좌 객체는 독립적인 잔액을 유지한다.

ref(BankAccount) Personal, Business;

Personal :- new BankAccount(100.0);
Business :- new BankAccount(1000.0);

Personal.Deposit(50.0);
Business.Withdraw(200.0);

OutFix(Personal.Balance, 2, 10);
OutImage;

OutFix(Business.Balance, 2, 10);
OutImage;

초기 Simula 67에서는 클래스 속성에 대한 외부 접근이 기본적으로 비교적 개방적이었다. 후기 표준에서는 hiddenprotected 명세를 통해 내부 표현과 상속 계층의 접근을 제한할 수 있게 되었다.

class BankAccount(InitialBalance);
    value InitialBalance;
    real InitialBalance;
hidden Balance;
begin
    real Balance;

    procedure Deposit(Amount);
        value Amount;
        real Amount;
    begin
        if Amount > 0.0 then
            Balance := Balance + Amount
    end;

    real procedure GetBalance;
        GetBalance := Balance;

    Balance := InitialBalance
end;

외부에서는 숨겨진 Balance를 직접 변경하는 대신 공개된 절차를 사용한다. 다만 Simula의 초기 객체 모델을 현대 언어의 private 중심 캡슐화와 완전히 동일하게 보는 것은 적절하지 않다. Simula에서 클래스의 핵심 목적은 먼저 현실의 개념과 동작을 하나의 모델링 단위로 표현하는 것이었고, 강한 정보 은닉은 이후 표준에서 보강되었다.

클래스 계층

Simula는 기존 클래스를 새 클래스의 접두부로 지정해 클래스 계층을 구성한다. 이를 prefixing이라고 하며, 후대의 객체 지향 언어에서는 주로 클래스 상속이라고 부른다.

class Vehicle(Name);
    text Name;
begin
    real Position, Speed;

    procedure Move(Duration);
        value Duration;
        real Duration;
    begin
        Position := Position + Speed * Duration
    end
end;

Vehicle class Car(Name, Seats);
    text Name;
    integer Seats;
begin
    Boolean EngineRunning;

    procedure Start;
        EngineRunning := true
end;

Car 객체는 Vehicle에 선언된 Name, Position, SpeedMove를 포함하며, 자신의 Seats, EngineRunningStart를 추가한다.

ref(Car) FamilyCar;

FamilyCar :- new Car("Example", 5);
FamilyCar.Speed := 20.0;
FamilyCar.Start;
FamilyCar.Move(3.0);

OutFix(FamilyCar.Position, 2, 10);
OutImage;

Simula의 접두 구조는 단순한 소스 코드 복사가 아니다. 접두 클래스와 하위 클래스의 속성은 하나의 객체 안에 결합되고, 객체의 타입은 클래스 계층 전체와 연결된다.

class Entity;
begin
    integer Identifier
end;

Entity class MovingEntity;
begin
    real X, Y
end;

MovingEntity class Aircraft;
begin
    real Altitude
end;

Aircraft 객체는 EntityMovingEntity의 객체가 별도로 포함된 조합이 아니라, 세 클래스 수준의 속성을 모두 가진 하나의 객체다.

Simula는 하나의 직접 접두 클래스만 지정할 수 있으므로 클래스 상속은 단일 상속 구조다. 여러 단계의 접두 구조를 통해 긴 계층을 만들 수 있으며, 하위 클래스는 상위 클래스 객체가 필요한 문맥에서 사용될 수 있다.

상위 클래스 참조와 하위 클래스 객체

상위 클래스 타입의 참조는 해당 클래스나 하위 클래스의 객체를 가리킬 수 있다.

ref(Vehicle) AnyVehicle;
ref(Car) MyCar;

MyCar :- new Car("Family car", 5);
AnyVehicle :- MyCar;

AnyVehicle.Speed := 15.0;
AnyVehicle.Move(2.0);

AnyVehicle의 정적 타입은 ref(Vehicle)이므로 Vehicle에 선언된 속성만 직접 사용할 수 있다. 그러나 실제 객체는 Car이므로 하나의 객체 안에 SeatsEngineRunning도 존재한다.

하위 클래스 속성에 접근하려면 qua로 참조를 한정할 수 있다.

ref(Vehicle) AnyVehicle;
ref(Car) SpecificCar;

AnyVehicle :- new Car("Taxi", 4);
SpecificCar :- AnyVehicle qua Car;

SpecificCar.Start;

qua Car는 참조가 실제로 Car와 호환되는 객체를 가리키는지 확인한다. 호환되지 않는 객체라면 실행 오류가 발생한다.

실제 타입이 확실하지 않은 경우에는 inspectwhen을 사용해 안전하게 분기할 수 있다.

inspect AnyVehicle
when Car do
begin
    OutText("Car with seats: ");
    OutInt(Seats, 3);
    OutImage
end
otherwise
begin
    OutText("Another kind of vehicle");
    OutImage
end;

when Car가 선택된 범위 안에서는 검사 대상 객체의 Car 속성을 직접 사용할 수 있다. 이 구조는 현대 언어의 타입 검사, 패턴 매칭과 하향 형변환을 결합한 형태와 유사하다.

상속과 확장

상속은 기존 클래스의 코드를 그대로 이용하는 기능이면서, 더 구체적인 개념을 기존 개념의 확장으로 정의하는 모델링 수단이다.

class Shape;
begin
    real X, Y;

    procedure MoveTo(NewX, NewY);
        value NewX, NewY;
        real NewX, NewY;
    begin
        X := NewX;
        Y := NewY
    end
end;

Shape class Circle(Radius);
    real Radius;
begin
    real procedure Area;
        Area := 3.141592653589793 * Radius ** 2
end;

Shape class Rectangle(Width, Height);
    real Width, Height;
begin
    real procedure Area;
        Area := Width * Height
end;

CircleRectangle은 모두 위치와 이동 기능을 공유하지만 넓이를 계산하는 방식과 고유 데이터는 다르다. 클래스 계층은 프로그램의 구현 관계인 동시에 ‘원은 도형이다’, ‘직사각형은 도형이다’와 같은 개념 관계를 표현한다.

그러나 상속이 모든 포함 관계를 나타내는 것은 아니다. 자동차가 엔진을 가진다는 관계를 표현하기 위해 CarEngine의 하위 클래스로 만들기보다, 자동차 객체가 엔진 객체의 참조를 가지도록 구성할 수 있다.

class Engine(Power);
    integer Power;
begin
    Boolean Running;

    procedure Start;
        Running := true;

    procedure Stop;
        Running := false
end;

class Car;
begin
    ref(Engine) Motor;

    Motor :- new Engine(150)
end;

Simula는 상속과 객체 참조를 함께 제공했기 때문에, 계층적 관계와 객체 사이의 연결 관계를 각각 다른 구조로 모델링할 수 있었다.

가상 절차

Simula의 가상 절차는 상위 클래스에 선언된 연산이 실제 객체의 클래스에 따라 다른 구현을 실행할 수 있도록 한다. 이는 다형성과 동적 디스패치의 핵심 기반이다.[53]

class Shape;
virtual:
    procedure Draw;
begin
    procedure Draw;
    begin
        OutText("Shape");
        OutImage
    end
end;

Shape class Circle(Radius);
    real Radius;
begin
    procedure Draw;
    begin
        OutText("Circle with radius ");
        OutFix(Radius, 2, 8);
        OutImage
    end
end;

Shape class Rectangle(Width, Height);
    real Width, Height;
begin
    procedure Draw;
    begin
        OutText("Rectangle ");
        OutFix(Width, 2, 8);
        OutText(" x ");
        OutFix(Height, 2, 8);
        OutImage
    end
end;

상위 클래스 타입의 참조를 통해 같은 절차 이름을 호출해도 실제 객체에 대응하는 구현이 선택된다.

ref(Shape) CurrentShape;

CurrentShape :- new Circle(5.0);
CurrentShape.Draw;

CurrentShape :- new Rectangle(10.0, 4.0);
CurrentShape.Draw;

첫 번째 Draw 호출은 Circle의 구현을 실행하고, 두 번째 호출은 Rectangle의 구현을 실행한다. 호출식은 같지만 실행되는 절차는 객체의 실제 클래스에 따라 결정된다.

가상 절차가 없다면 호출자는 객체의 종류를 직접 검사하고 각 클래스에 맞는 절차를 선택해야 한다.

inspect CurrentShape
when Circle do
    DrawCircle(CurrentShape qua Circle)
when Rectangle do
    DrawRectangle(CurrentShape qua Rectangle);

가상 절차를 사용하면 이러한 타입 분기가 클래스 계층 안으로 이동한다.

CurrentShape.Draw;

새로운 하위 클래스를 추가하더라도 호출부를 계속 수정하지 않고, 새 클래스가 Draw 구현을 제공하도록 확장할 수 있다.

다형적인 객체 집합

상위 클래스 참조를 이용하면 서로 다른 하위 클래스 객체를 하나의 자료 구조에서 다룰 수 있다.

ref(Shape) array Shapes(1:3);

Shapes(1) :- new Circle(3.0);
Shapes(2) :- new Rectangle(4.0, 5.0);
Shapes(3) :- new Circle(10.0);

각 요소의 정적 참조 타입은 Shape지만 실제 객체의 클래스는 서로 다르다.

integer Index;

for Index := 1 step 1 until 3 do
    Shapes(Index).Draw;

반복문은 각 객체의 실제 클래스를 알 필요가 없다. Draw 호출은 가상 절차 바인딩에 따라 적절한 구현으로 연결된다.

이 구조는 프로그램을 특정 객체 종류의 목록이 아니라 공통 인터페이스를 가진 객체의 집합으로 구성할 수 있게 한다. Simula의 용어 체계는 현대의 인터페이스나 추상 클래스와 정확히 일치하지 않지만, 상위 클래스의 가상 절차를 통해 공통 연산 계약을 정의하고 여러 하위 클래스가 다른 구현을 제공하는 구조를 이미 갖추고 있었다.

가상 바인딩

가상 절차의 호출 대상은 참조 변수의 선언 타입만으로 결정되지 않는다. 호출 시점에 참조가 가리키는 객체의 클래스 계층에서 가장 구체적으로 연결된 구현을 찾는다.

class Animal;
virtual:
    procedure Speak;
begin
    procedure Speak;
    begin
        OutText("Animal");
        OutImage
    end
end;

Animal class Dog;
begin
    procedure Speak;
    begin
        OutText("Dog");
        OutImage
    end
end;

Dog class GuideDog;
begin
    procedure Speak;
    begin
        OutText("Guide dog");
        OutImage
    end
end;
ref(Animal) A;

A :- new GuideDog;
A.Speak;

A의 선언 타입은 Animal이지만 실제 객체는 GuideDog이므로 가장 구체적인 GuideDog.Speak가 실행된다.

이러한 가상 바인딩은 객체의 내부 종류를 호출자로부터 분리한다. 호출자는 Animal이 제공하는 연산만 알면 되고, 실제 동작은 객체가 결정한다.

inner와 클래스 본문의 확장

Simula의 상속은 속성과 가상 절차뿐 아니라 클래스 본문의 실행문도 결합한다. 상위 클래스 본문에 있는 inner는 하위 클래스에서 추가한 본문을 실행하는 확장 지점이다.

class Document;
begin
    OutText("Open document");
    OutImage;

    inner;

    OutText("Close document");
    OutImage
end;

Document class TextDocument;
begin
    OutText("Process text");
    OutImage
end;

new TextDocument를 실행하면 다음 순서로 클래스 본문이 결합된다.

Open document
Process text
Close document

현대 언어에서는 하위 클래스 생성자가 상위 클래스 생성자를 호출하거나 하위 클래스가 super를 통해 상위 구현을 호출하는 방식이 일반적이다. Simula의 inner는 반대 방향으로 동작한다. 상위 클래스가 미리 정의한 실행 구조 안에서 하위 클래스의 추가 동작을 호출한다.

여러 단계의 클래스 계층에서는 inner가 계층을 따라 안쪽으로 들어갔다가 되돌아온다.

class A;
begin
    OutText("A before");
    OutImage;

    inner;

    OutText("A after");
    OutImage
end;

A class B;
begin
    OutText("B before");
    OutImage;

    inner;

    OutText("B after");
    OutImage
end;

B class C;
begin
    OutText("C");
    OutImage
end;

new C의 실행 순서는 다음과 같다.

A before
B before
C
B after
A after

inner는 상위 클래스가 전체 알고리즘의 골격을 제공하고 하위 클래스가 일부 단계를 채우는 템플릿 메서드 패턴과 비슷한 구조를 언어 자체에서 제공한다.

this와 객체 문맥

클래스 내부에서는 this 클래스명으로 현재 객체를 참조할 수 있다.

class Node;
begin
    ref(Node) Next;

    procedure Connect(Other);
        ref(Node) Other;
    begin
        this Node.Next :- Other
    end
end;

현대 객체 지향 언어의 this 또는 self와 달리 Simula에서는 참조할 클래스 수준을 함께 지정한다. 클래스가 중첩되거나 접두 계층 안에 있을 때 어떤 활성 객체를 가리키는지 명시할 수 있다.

class Container;
begin
    class Element;
    begin
        ref(Container) Owner;

        Owner :- this Container
    end
end;

Element 객체는 자신이 생성된 외부 Container 객체를 this Container로 참조할 수 있다.

중첩 클래스와 외부 객체

Simula에서는 클래스를 다른 클래스 안에 선언할 수 있다. 내부 클래스의 객체는 자신을 둘러싼 외부 객체의 속성에 접근할 수 있다.

class Graph;
begin
    integer NextIdentifier;

    class Vertex;
    begin
        integer Identifier;

        NextIdentifier := NextIdentifier + 1;
        Identifier := NextIdentifier
    end;

    ref(Vertex) procedure NewVertex;
        NewVertex :- new Vertex
end;

Graph 객체는 자신만의 NextIdentifierVertex 클래스 문맥을 가진다.

ref(Graph) FirstGraph, SecondGraph;

FirstGraph :- new Graph;
SecondGraph :- new Graph;

FirstGraph.NewVertex;
FirstGraph.NewVertex;
SecondGraph.NewVertex;

첫 번째 그래프에서 생성된 정점의 식별자는 1과 2가 되고, 두 번째 그래프의 첫 정점 식별자는 다시 1이 된다. 내부 객체는 자신이 속한 외부 객체의 환경과 연결된다.

이 구조는 클래스가 전역적인 타입 정의에 그치지 않고, 객체의 문맥 안에서 새로운 객체 종류를 정의할 수 있음을 보여준다. Simula의 중첩 클래스 개념은 이후 BETA의 패턴과 중첩 가상 구조로 확장되었다.

객체 사이의 연결

복잡한 모델은 객체 하나만으로 구성되지 않는다. Simula에서 객체는 다른 객체의 참조를 속성으로 보관해 그래프 형태의 관계를 만든다.

class Employee(Name);
    text Name;
begin
    ref(Department) DepartmentReference
end;

class Department(Name);
    text Name;
begin
    ref(Employee) Manager
end;
ref(Employee) EmployeeObject;
ref(Department) DepartmentObject;

EmployeeObject :- new Employee("Dahl");
DepartmentObject :- new Department("Research");

EmployeeObject.DepartmentReference :- DepartmentObject;
DepartmentObject.Manager :- EmployeeObject;

직원은 자신이 속한 부서를 가리키고 부서는 관리자를 가리킨다. Simula의 객체 모델은 계층적 상속뿐 아니라 이러한 임의의 객체 연결을 통해 현실의 관계를 표현한다.

SIMSET 시스템 클래스는 객체를 양방향 연결 목록에 넣기 위한 LinkHead를 제공했다. 객체 참조와 연결 목록은 시뮬레이션의 대기열, 사건 목록과 자원 집합을 구성하는 데 사용되었다.

능동 객체

Simula 객체는 데이터와 호출 가능한 절차만 가진 수동 객체로 제한되지 않는다. 클래스 본문 자체가 독립적인 실행 흐름을 가지고 detach, callresume을 사용해 중단과 재개를 수행할 수 있다.

class Worker(Name);
    text Name;
begin
    integer Step;

    for Step := 1 step 1 until 3 do
    begin
        OutText(Name);
        OutText(" performs step ");
        OutInt(Step, 1);
        OutImage;

        detach
    end
end;

Worker 객체는 Step의 값과 실행 위치를 보존한다. 객체가 다시 활성화되면 이전 detach 다음부터 실행을 계속한다.

시뮬레이션 시스템의 Process는 이러한 준병렬 객체 모델을 기반으로 한다. 고객, 기계와 차량 객체는 자신의 행동 순서를 클래스 본문에 연속적으로 작성하고, Hold, WaitPassivate에서 실행을 중단한 뒤 사건 목록에 따라 다시 실행된다.

이 점에서 Simula의 객체는 후대의 클래스 기반 언어에서 일반화된 수동 객체보다 넓은 개념이었다. 객체는 필요한 경우 자체 실행 상태를 가진 프로세스가 될 수 있었다.

객체의 생명 주기

객체는 new로 생성되며 참조를 통해 접근된다. 명시적으로 객체를 삭제하는 연산은 제공되지 않고, 더 이상 접근할 수 없는 객체의 저장 공간은 가비지 컬렉션으로 회수될 수 있다.[54]

ref(Person) Current;

Current :- new Person("First", 1900);
Current :- new Person("Second", 1950);

첫 번째 객체를 가리키는 다른 참조가 없다면 두 번째 대입 뒤에는 프로그램에서 첫 번째 객체에 도달할 수 없다. 런타임은 해당 객체를 회수할 수 있다.

객체의 실행 종료와 저장 공간의 수명은 서로 구분된다. 클래스 본문이 끝난 객체도 참조가 남아 있다면 속성에 접근할 수 있다.

ref(Counter) C;

C :- new Counter(10);

! 클래스 본문 실행이 끝났어도 객체는 참조된다.;
C.Increment;
OutInt(C.Value, 3);
OutImage;

준병렬 객체가 detach되어 있다면 클래스 본문은 아직 종료되지 않았으며 나중에 실행을 계속할 수 있다. 종료된 객체는 실행을 다시 이어갈 수 없지만 저장된 데이터와 절차 속성은 참조가 유지되는 동안 사용할 수 있다.

캡슐화

Simula가 제공한 캡슐화는 데이터와 연산을 같은 객체에 묶는 구조, 클래스에 따른 이름 범위, 객체 참조를 통한 접근과 후기 표준의 접근 제한으로 구성된다.

class Thermometer;
hidden Temperature;
begin
    real Temperature;

    procedure SetTemperature(Value);
        value Value;
        real Value;
    begin
        if Value < -273.15 then
            Temperature := -273.15
        else
            Temperature := Value
    end;

    real procedure GetTemperature;
        GetTemperature := Temperature
end;

외부 코드는 객체의 내부 표현을 직접 바꾸는 대신 객체가 제공하는 절차를 호출한다.

ref(Thermometer) Sensor;

Sensor :- new Thermometer;
Sensor.SetTemperature(25.0);

OutFix(Sensor.GetTemperature, 2, 8);
OutImage;

이 구조는 객체가 자신의 불변 조건과 상태 변경 규칙을 관리하도록 한다. 그러나 Simula의 클래스 설계는 단순한 정보 은닉보다 시스템 모델링에 더 넓은 목적을 가졌다. 객체는 외부 세계로부터 완전히 닫힌 단위라기보다, 현실의 구성 요소와 관계를 명시적으로 나타내는 프로그램 요소였다.

상속과 코드 재사용

접두 클래스는 여러 클래스가 공통 데이터와 절차를 공유하도록 한다.

class NamedObject(Name);
    text Name;
begin
    procedure PrintName;
    begin
        OutText(Name);
        OutImage
    end
end;

NamedObject class Customer(Name, CustomerNumber);
    text Name;
    integer CustomerNumber;
begin
    real AccountBalance
end;

NamedObject class Product(Name, Price);
    text Name;
    real Price;
begin
    integer Stock
end;

CustomerProduct는 모두 이름 저장과 출력 기능을 재사용한다.

상속의 역할은 코드 중복 제거에 한정되지 않는다. 상위 클래스 타입을 공통 추상화로 사용하고 가상 절차를 호출하려면, 하위 클래스 객체가 상위 클래스와 의미 있는 대체 관계를 가져야 한다.

ref(NamedObject) Item;

Item :- new Customer("Customer A", 1001);
Item.PrintName;

Item :- new Product("Product A", 9.99);
Item.PrintName;

이처럼 Simula의 상속은 구현 재사용, 타입 호환성과 동적 바인딩을 하나의 클래스 계층으로 묶는다.

다형성

Simula에서 다형성은 주로 다음 구조의 결합으로 실현된다.

  • 상위 클래스 참조가 하위 클래스 객체를 가리킬 수 있다.
  • 클래스 계층에서 공통 가상 절차를 선언할 수 있다.
  • 호출 시 실제 객체의 클래스에 따라 절차 구현이 선택된다.
  • 여러 하위 클래스 객체를 상위 클래스 타입으로 함께 다룰 수 있다.

다음 예제는 서로 다른 종류의 메시지 객체를 공통 참조로 처리한다.

class Message;
virtual:
    procedure Send;
begin
    procedure Send;
    begin
        OutText("Generic message");
        OutImage
    end
end;

Message class Email(Address);
    text Address;
begin
    procedure Send;
    begin
        OutText("Send email to ");
        OutText(Address);
        OutImage
    end
end;

Message class PrintJob(PrinterName);
    text PrinterName;
begin
    procedure Send;
    begin
        OutText("Send document to ");
        OutText(PrinterName);
        OutImage
    end
end;
ref(Message) M;

M :- new Email("[email protected]");
M.Send;

M :- new PrintJob("Office printer");
M.Send;

호출부는 메시지의 구체적인 종류를 구분하지 않는다. 각 객체가 자신의 Send 구현을 제공한다.

객체의 생명 주기와 모델링

Simula에서 클래스는 객체의 데이터 구조뿐 아니라 생성 이후 수행할 행동도 정의한다. 시뮬레이션 객체는 생성, 활성화, 대기, 재활성화와 종료의 생명 주기를 클래스 본문 안에 직접 나타낼 수 있다.

Process class Customer(Name);
    text Name;
begin
    OutText(Name);
    OutText(" arrives");
    OutImage;

    Wait(Queue);

    OutText(Name);
    OutText(" receives service");
    OutImage;

    Hold(5.0);

    OutText(Name);
    OutText(" leaves");
    OutImage
end;

고객 객체의 정적 상태와 시간에 따른 행동이 하나의 클래스 안에 함께 존재한다. WaitHold에서 실행이 중단되더라도 객체의 지역 데이터와 실행 위치는 보존된다.

이러한 구조는 객체를 단순히 함수를 묶어 호출하는 데이터 컨테이너가 아니라, 현실의 대상에 대응하는 지속적인 존재로 다룬다. 객체는 생성되고, 상태가 변하고, 다른 객체와 관계를 맺고, 행동하며, 최종적으로 더 이상 접근되지 않을 때 소멸한다.

객체 지향 모델링

Simula의 설계자들은 프로그램 구조와 문제 영역의 개념 구조를 가깝게 대응시키려 했다. 시뮬레이션 대상의 종류는 클래스로, 개별 대상은 객체로, 종류 사이의 일반화 관계는 클래스 계층으로, 대상 사이의 관계는 객체 참조로 표현한다.[55]

예를 들어 교통 모델은 다음과 같이 구성할 수 있다.

class Road(Name, Length);
    text Name;
    real Length;
begin
    ref(Vehicle) FirstVehicle
end;

Process class Vehicle(Registration);
    text Registration;
begin
    ref(Road) CurrentRoad;
    real Position, Speed;

    while CurrentRoad =/= none do
    begin
        Hold(1.0);
        Position := Position + Speed
    end
end;

도로와 차량은 서로 다른 클래스이며, 각 차량 객체는 현재 도로 객체를 참조한다. 차량은 자신의 위치와 속도를 보관하고 독립적인 실행 흐름으로 이동한다.

클래스 계층은 종류 사이의 일반화 관계를 나타낸다.

Vehicle class Bus(Registration, Capacity);
    text Registration;
    integer Capacity;
begin
    integer Passengers
end;

Vehicle class Truck(Registration, MaximumLoad);
    text Registration;
    real MaximumLoad;
begin
    real CurrentLoad
end;

버스와 트럭은 차량의 공통 상태와 이동 행동을 공유하면서 각 종류에 필요한 속성을 추가한다.

객체 참조는 대상 사이의 연관 관계를 나타낸다.

class Driver(Name);
    text Name;
begin
    ref(Vehicle) AssignedVehicle
end;

이러한 모델링 방식은 프로그램을 자료와 절차의 전역적인 집합으로만 구성하지 않고, 문제 영역에 존재하는 개념과 관계를 중심으로 조직한다.

추상화 단위로서의 객체

Simula의 객체는 다음 요소를 하나의 추상화 단위에 결합한다.

  • 객체가 보유한 상태
  • 상태를 읽고 변경하는 절차
  • 객체를 식별하는 참조
  • 다른 객체와 연결되는 관계
  • 클래스 계층에서의 타입 위치
  • 가상 절차에 의해 선택되는 동작
  • 필요한 경우 중단하고 재개할 수 있는 실행 상태
  • 생성부터 회수까지 이어지는 동적 수명

클래스는 이러한 객체의 공통 패턴을 정의한다. 상위 클래스는 여러 종류가 공유하는 일반적인 특성을 나타내고, 하위 클래스는 더 구체적인 데이터와 동작을 추가한다.

Simula의 객체 지향 구조는 프로그램을 단순히 클래스 계층으로 분류하는 것에 머물지 않는다. 객체는 실행 중 동적으로 생성되고 참조를 통해 연결되므로, 실제 프로그램 구조는 객체들로 구성된 동적 그래프가 된다. 클래스 계층은 가능한 객체 종류의 정적 관계를 나타내고, 참조 관계는 특정 실행 시점에 존재하는 객체 사이의 연결을 나타낸다.

객체 지향이라는 용어

Simula 67이 설계되던 당시 달과 뉘고르는 처음부터 자신들의 작업을 ‘객체 지향 프로그래밍’이라는 이름으로 체계화하지 않았다. 클래스, 객체와 상속은 시뮬레이션 모델을 구성하는 과정에서 만들어졌으며, 초기 문서에서는 주로 class, object, subclass, prefixing과 virtual quantity 같은 구체적인 언어 요소로 설명되었다.

‘객체 지향’이라는 용어는 이후 앨런 케이Smalltalk 계열에서 널리 알려졌다. Smalltalk은 객체 사이의 메시지 전송과 일관된 객체 환경을 중심으로 객체 지향 개념을 다시 구성했지만, 클래스와 인스턴스, 상속을 통한 특수화라는 핵심 계보는 Simula의 영향을 받았다.

따라서 객체 지향 프로그래밍의 역사는 하나의 고정된 정의가 한 번에 만들어진 과정으로 보기 어렵다. Simula는 클래스 기반 객체 모델과 시뮬레이션 대상의 모델링을 확립했고, Smalltalk은 객체와 메시지를 중심으로 한 대화형 환경을 발전시켰으며, 이후 C++, JavaC# 등은 각자의 실행 환경과 사용 목적에 맞게 이 개념을 다시 구성했다.

현대 객체 지향 언어와의 차이

Simula는 현대의 클래스 기반 객체 지향 언어와 많은 공통점을 가지지만 동일한 구조는 아니다.

개념Simula현대 언어의 일반적인 형태
객체 생성new Class(...)new Class(...) 또는 생성 함수
객체 참조 대입:-일반적으로 =
값 대입:=일반적으로 =
상속접두 클래스 Base class Derivedextends, : Base
동적 바인딩virtual: 명세의 가상 절차virtual, override, 기본 가상 메서드
현재 객체this ClassNamethis 또는 self
안전한 타입 분기inspect ... when패턴 매칭, instanceof, 타입 검사
하위 타입 한정qua명시적 캐스트
상속 본문 결합inner생성자 연결, super, 템플릿 메서드
실행 상태클래스 객체가 중단·재개 가능보통 코루틴이나 비동기 기능으로 분리
메모리 관리가비지 컬렉션가비지 컬렉션, 참조 계수 또는 수동 관리

후대의 언어들은 Simula의 클래스, 객체, 상속과 가상 절차를 계승하면서도 inner 기반 본문 결합과 객체 자체의 준병렬 실행은 대부분 별도의 기능으로 분리했다. 생성자, 메서드 재정의, 스레드, 코루틴과 비동기 함수가 Simula 객체가 함께 가지고 있던 역할의 일부를 나누어 담당한다.

역사적 의미

Simula가 객체 지향 프로그래밍의 출발점으로 평가되는 이유는 classobject라는 단어를 처음 사용했다는 사실에만 있지 않다. 프로그램 실행 중 독립적인 정체성과 수명을 가진 객체를 생성하고, 객체가 자신의 데이터와 동작을 보유하며, 클래스 계층과 가상 절차를 통해 공통 인터페이스와 서로 다른 구현을 결합하는 모델을 완성했다는 점에 있다.[56]

Simula의 객체 모델은 이후 언어 설계뿐 아니라 객체 지향 분석 및 설계, 소프트웨어 모델링과 프레임워크 구성에도 영향을 주었다. 현실의 개념을 객체로 식별하고, 공통 특성을 클래스로 추출하며, 관계와 행동을 프로그램 구조에 대응시키는 방식은 대규모 소프트웨어 설계의 주요 접근법으로 발전했다.

달과 뉘고르는 Simula를 통해 객체를 단순한 기술적 저장 단위가 아니라 복잡한 시스템을 이해하고 설명하는 개념적 도구로 제시했다. Simula에서 프로그래밍과 모델링은 분리된 활동이 아니었으며, 프로그램은 실행 가능한 계산 절차인 동시에 대상 시스템에 대한 형식적인 설명이었다.

이러한 관점에서 Simula의 객체 지향 프로그래밍은 코드 재사용을 위한 클래스 문법보다 넓은 의미를 가진다. 객체는 현실 또는 개념 세계의 요소를 나타내고, 클래스는 공통 성질을 정의하며, 상속은 개념의 특수화를 표현하고, 가상 절차는 동일한 요청에 객체 종류별 행동을 연결한다. 실행 중 생성된 객체의 집합과 관계는 모델이 표현하는 시스템의 현재 상태를 이룬다.

문법

Simula의 문법은 ALGOL 60의 블록 구조와 선언 방식을 기반으로 한다. 프로그램은 beginend로 둘러싸인 블록으로 구성되며, 변수·배열·절차와 클래스는 일반적으로 사용되는 문장보다 앞에서 선언한다. 문장은 세미콜론으로 구분하고, 이름의 유효 범위는 블록과 클래스의 정적 중첩 구조에 따라 결정된다.[57]

Simula 67은 ALGOL 60에 클래스, 객체 참조, 동적 객체 생성, 접두 클래스, 가상 절차, 텍스트와 준병렬 실행 문법을 추가했다. 일반 값 대입에는 :=, 객체와 텍스트의 참조 대입에는 :-를 사용한다. 언어는 대소문자를 구분하지 않으므로 begin, BEGINBegin은 같은 키워드로 인식된다.[58]

어휘 구조

Simula 프로그램은 식별자, 키워드, 리터럴, 연산자와 구분 기호로 구성된다. 키워드는 대소문자를 구분하지 않는다.

begin
    integer Count;

    Count := 10;

    OutInt(count, 3);
    OutImage
end;

Countcount는 같은 식별자를 나타낸다. 관례적으로 표준 문서에서는 키워드의 첫 글자를 대문자로 쓰거나 모두 소문자로 쓰는 방식이 혼용되지만, 의미 차이는 없다.

식별자는 문자로 시작하고 문자와 숫자를 이어서 작성한다. 구현에 따라 식별자 길이의 처리 한계가 있을 수 있으므로, 서로 다른 이름의 앞부분이 지나치게 길게 같아지는 것은 피하는 편이 이식성에 유리하다.

integer CustomerCount;
real AverageWaitingTime;
ref(Customer) CurrentCustomer;

공백, 줄바꿈과 주석은 일반적으로 토큰을 구분하는 역할을 한다. 문자열과 문자 리터럴 안의 공백은 그대로 값에 포함된다.

주석

Simula에는 느낌표 !로 시작해 다음 세미콜론까지 이어지는 주석 형식이 있다.

begin
    integer Count;

    ! 처리할 항목의 개수.;
    Count := 10;

    ! 결과를 화면에 출력한다.;
    OutInt(Count, 3);
    OutImage
end;

주석을 끝내는 세미콜론은 주석 자체에 포함된다. 따라서 주석 뒤에 이어지는 문장과 구분되도록 세미콜론 위치를 확인해야 한다.

Count := Count + 1; ! 항목 하나를 처리했다.;

일부 구현에서는 퍼센트 기호나 구현별 확장 주석을 지원하기도 하지만, 표준적인 Simula 소스에는 ! ... ; 형식을 사용하는 것이 적합하다.

프로그램 구조

가장 단순한 Simula 프로그램은 하나의 문장으로 구성할 수 있다. 빈 블록도 정상적인 프로그램이다.

begin
end;

일반적인 프로그램은 선언과 실행문을 포함하는 주 블록으로 작성한다.

begin
    integer Count;
    real Total;

    Count := 5;
    Total := Count * 2.5;

    OutText("Total: ");
    OutFix(Total, 2, 8);
    OutImage
end;

Simula 프로그램에는 C나 Java의 main 함수에 해당하는 별도 진입점이 없다. 최외곽 문장이나 블록 자체가 프로그램의 시작점이며, 블록의 첫 실행문부터 순서대로 실행한다.

최외곽 블록에 Simulation, Simset 또는 사용자 정의 클래스를 접두부로 지정할 수도 있다.

Simulation
begin
    OutText("Simulation starts at ");
    OutFix(Time, 2, 8);
    OutImage
end;

이 프로그램의 주 블록은 Simulation 클래스가 제공하는 Time, Process, Hold와 사건 처리 기능을 사용할 수 있다.

블록

블록은 beginend 사이에 선언과 실행문을 배치한 구조다.

begin
    integer OuterValue;

    OuterValue := 10;

    begin
        integer InnerValue;

        InnerValue := OuterValue * 2;
        OutInt(InnerValue, 4);
        OutImage
    end
end;

안쪽 블록은 바깥쪽 블록의 이름을 사용할 수 있지만, 바깥쪽 블록에서는 안쪽에 선언된 InnerValue를 사용할 수 없다.

같은 이름을 안쪽 블록에서 다시 선언하면 해당 범위에서는 안쪽 선언이 바깥쪽 선언을 가린다.

begin
    integer Value;

    Value := 10;

    begin
        integer Value;

        Value := 20;
        OutInt(Value, 3);
        OutImage
    end;

    OutInt(Value, 3);
    OutImage
end;

첫 출력은 20이고 두 번째 출력은 바깥쪽 변수의 값인 10이다.

기본 타입

Simula의 주요 기본 타입은 다음과 같다.

타입용도
integer정수
short integer비교적 작은 범위의 정수
real실수
long real더 넓은 정밀도의 실수
Boolean논리값
character문자 하나
text문자 프레임 또는 문자열
ref(ClassName)클래스 객체 참조
begin
    integer Count;
    short integer SmallNumber;
    real Distance;
    long real PreciseValue;
    Boolean Finished;
    character Grade;
    text Message;

    Count := 100;
    SmallNumber := 5;
    Distance := 12.5;
    PreciseValue := 3.141592653589793;
    Finished := false;
    Grade := 'A';
    Message :- "Simula"
end;

Boolean 값은 truefalse로 나타낸다. 객체 참조 타입은 클래스 이름을 포함한 ref(C) 형태로 작성한다.

정수와 실수 리터럴

정수 리터럴은 부호 없는 숫자의 연속으로 작성한다.

integer A, B;

A := 0;
B := 12345;

실수는 소수점이나 지수 표기를 사용할 수 있다.

real A, B, C;

A := 3.14;
B := 0.5;
C := 1.25&3;

전통적인 Simula 표기에서 &는 10의 거듭제곱을 나타내는 지수 표기에 사용된다. 1.25&3은 (1.25 \times 10^3)에 해당한다. 구현에 따라 현대적인 지수 표기를 추가로 지원할 수 있지만, 표준 소스에서는 언어 사양의 표기를 기준으로 한다.

long real 리터럴과 지수 표기는 구현이 지원하는 정밀도와 범위 안에서 처리된다.

문자 리터럴

문자 하나는 작은따옴표로 감싼다.

character Letter;

Letter := 'A';

OutChar(Letter);
OutImage;

문자 값은 문자 집합에서의 순서와 관련된 표준 절차로 변환할 수 있다.

integer Code;
character Symbol;

Code := Rank('A');
Symbol := Char(Code);

Rank는 문자의 순서값을 반환하고 Char는 정수값에 해당하는 문자를 반환한다.

텍스트 리터럴

텍스트 리터럴은 큰따옴표로 감싼다.

text Message;

Message :- "Hello, Simula";

OutText(Message);
OutImage;

긴 텍스트나 큰따옴표 자체를 포함하는 방식은 사용 중인 표준 및 구현의 텍스트 표기 규칙을 따라야 한다. 텍스트는 일반적인 객체 참조와는 별도의 타입이지만, 문자 저장 영역의 프레임을 가리키는 참조적 의미를 가진다.

변수 선언

변수 선언은 타입 뒤에 하나 이상의 식별자를 나열하는 방식으로 작성한다.

integer Count, Index;
real Width, Height, Area;
Boolean Available, Finished;
text Name, Description;

선언과 동시에 값을 지정하는 현대 언어식 초기화 문법은 일반적인 Simula 선언 방식이 아니다. 선언 뒤의 실행문에서 값을 대입한다.

integer Count;
real Price;

Count := 10;
Price := 9.95;

지역 변수의 초기값에 의존하기보다 사용 전에 명시적으로 값을 대입하는 편이 적합하다.

상수와 value

Simula에는 현대 언어의 일반적인 const 변수 선언과 같은 문법보다, 절차와 클래스 매개변수의 전달 의미를 지정하는 value 명세가 중요하게 사용된다.

procedure PrintRepeated(Message, Count);
    value Count;
    text Message;
    integer Count;
begin
    integer Index;

    for Index := 1 step 1 until Count do
    begin
        OutText(Message);
        OutImage
    end
end;

value CountCount를 값으로 전달한다는 의미다. 절차 내부에서 매개변수를 변경해도 호출자의 원래 변수에는 영향을 주지 않는다.

텍스트, 배열, 절차와 일부 매개변수의 전달 의미는 기본 타입과 다르므로, value, name과 타입 명세를 함께 확인해야 한다.

대입문

일반 값 대입에는 :=를 사용한다.

integer Count;
real Total;

Count := 10;
Total := Count * 2.5;

하나의 식 값을 여러 변수에 연속해서 대입할 수 있다.

integer A, B, C;

A := B := C := 0;

객체 참조와 텍스트 참조를 대입할 때는 :-를 사용한다.

ref(Customer) First, Second;

First :- new Customer("A");
Second :- First;

Second :- First는 객체를 복사하지 않고 두 참조가 같은 객체를 가리키게 한다.

텍스트에서 두 대입 연산은 서로 다른 의미를 갖는다.

text Source, Target;

Source :- "SIMULA";
Target :- Blanks(6);

Target := Source;

Target :- Source는 같은 텍스트 프레임을 참조하도록 만들고, Target := Source는 Source의 문자를 Target 프레임으로 복사한다.

산술 연산자

주요 산술 연산자는 다음과 같다.

연산자의미
+덧셈 또는 단항 양수
-뺄셈 또는 단항 음수
*곱셈
/실수 나눗셈
//정수 나눗셈
**거듭제곱
integer Quotient;
real Average, Power;

Quotient := 17 // 5;
Average := 17 / 5;
Power := 2 ** 8;

정수 나눗셈 //은 정수 몫을 구한다. /는 피연산자와 문맥에 따라 실수 계산에 사용된다.

연산 순서가 불분명하거나 복잡하면 괄호를 사용한다.

Result := (A + B) * (C - D) / E;

관계 연산자

값의 비교에는 다음 연산자를 사용한다.

연산자의미
=같음
<>같지 않음
<작음
<=작거나 같음
>
>=크거나 같음
if Count = 0 then
    OutText("Empty");

if Temperature >= 100.0 then
    OutText("Boiling");

객체 참조의 동일성 비교는 일반 값 비교와 구분되는 ===/=를 사용한다.

if First == Second then
    OutText("Same object");

if First =/= Third then
    OutText("Different objects");

First == Second는 두 객체의 속성 값이 같은지를 비교하는 것이 아니라, 두 참조가 동일한 객체를 가리키는지를 검사한다.

논리 연산자

Boolean 연산에는 not, and, or, impeqv 등을 사용할 수 있다.

Boolean Ready, Connected, CanStart;

CanStart := Ready and Connected;

if not CanStart then
    OutText("Cannot start");

imp는 논리적 함의를, eqv는 논리적 동치를 나타낸다.

Boolean A, B;

if A imp B then
    OutText("A implies B");

if A eqv B then
    OutText("A and B have the same truth value");

표준에는 완전 평가와 단축 평가를 구분하는 연산 형태가 존재한다. 조건식에서 오른쪽 피연산자의 평가 여부가 중요할 때는 사용하는 구현과 표준의 연산 정의를 확인해야 한다.

조건문

기본 조건문은 if ... then 형식이다.

if Temperature < 0.0 then
    OutText("Below freezing");

대체 경로는 else로 지정한다.

if Balance >= Amount then
    Balance := Balance - Amount
else
    OutText("Insufficient balance");

여러 문장을 조건부로 실행하려면 블록을 사용한다.

if Balance >= Amount then
begin
    Balance := Balance - Amount;

    OutText("Withdrawal completed");
    OutImage
end
else
begin
    OutText("Insufficient balance");
    OutImage
end;

중첩된 if에서 else는 가장 가까운 아직 else가 없는 if와 결합한다. 의도를 분명하게 표현하려면 beginend로 범위를 명시하는 편이 적합하다.

조건식

Simula에서는 if 구조를 값을 생성하는 조건식으로 사용할 수 있다.

integer Maximum;

Maximum := if A > B then A else B;

조건식의 thenelse 부분은 호환 가능한 타입의 값을 생성해야 한다.

text Status;

Status :-
    if Finished then
        "Finished"
    else
        "Running";

객체 참조를 선택하는 식에도 사용할 수 있다.

Current :-
    if UsePrimary then
        Primary
    else
        Secondary;

while 반복문

조건이 참인 동안 문장을 반복하려면 while을 사용한다.

integer Count;

Count := 1;

while Count <= 5 do
begin
    OutInt(Count, 3);
    OutImage;

    Count := Count + 1
end;

조건은 각 반복 전에 검사된다. 처음부터 거짓이면 본문은 한 번도 실행되지 않는다.

for 반복문

Simula의 for 문은 제어 변수와 하나 이상의 반복 요소로 구성된다.

integer Index;

for Index := 1 step 1 until 10 do
begin
    OutInt(Index, 3);
    OutImage
end;

step은 증감량을 지정하고 until은 마지막 범위를 지정한다.

integer Index;

for Index := 10 step -1 until 1 do
begin
    OutInt(Index, 3);
    OutImage
end;

쉼표로 여러 값을 직접 나열할 수도 있다.

integer Value;

for Value := 1, 2, 5, 10, 20 do
begin
    OutInt(Value, 4);
    OutImage
end;

값 목록과 step ... until 요소를 함께 사용할 수도 있다.

integer Value;

for Value := 0, 10 step 10 until 100 do
begin
    OutInt(Value, 4);
    OutImage
end;

while 요소를 사용하면 반복 제어식을 조건과 결합할 수 있다.

integer Index;

for Index := Index + 1 while Index < Limit do
    Process(Index);

제어 변수의 값과 반복 종료 이후의 상태는 표준 규칙에 따라 결정되므로, 반복문 이후 값에 의존하는 코드는 명확하게 다시 대입하는 편이 좋다.

goto와 레이블

Simula는 ALGOL 계열의 레이블과 goto 문을 지원한다.

begin
    integer Count;

Start:
    Count := Count + 1;

    if Count < 5 then
        goto Start;

    OutText("Finished");
    OutImage
end;

레이블은 문장 앞에 식별자와 콜론을 작성해 정의한다.

Retry:
    ReadInput;

레이블을 변수처럼 다루는 label 타입과 여러 레이블을 선택하는 switch 선언도 존재한다.

label Destination;

Destination := ExitPoint;
goto Destination;

ExitPoint:
    OutText("Exited");

현대적인 구조화 코드에서는 while, for, 절차와 조건문으로 대부분의 제어 흐름을 표현할 수 있지만, 레이블과 goto는 표준 문법의 일부다.

switch

switch는 정수 인덱스에 따라 여러 설계적 표현 가운데 하나를 선택하는 구조다.

switch Destination :=
    FirstLabel,
    SecondLabel,
    ThirdLabel;

integer Choice;

goto Destination(Choice);

FirstLabel:
    OutText("First");
    goto Finish;

SecondLabel:
    OutText("Second");
    goto Finish;

ThirdLabel:
    OutText("Third");

Finish:
    OutImage;

switch는 현대 언어의 switch 문과 이름은 같지만, 여러 문장 분기를 직접 포함하는 구조라기보다 레이블을 선택하는 설계적 표현에 가깝다.

배열 선언

배열은 배열 이름 뒤의 괄호 안에 각 차원의 하한과 상한을 작성한다.

integer array Values(1:10);
real array Matrix(1:3, 1:4);

Simula 배열은 인덱스가 반드시 0이나 1에서 시작할 필요가 없다.

integer array Temperature(-10:40);

Temperature(-10) := 5;
Temperature(40) := 30;

배열 요소는 배열 이름 뒤의 괄호로 접근한다.

integer Index;
real Sum;
real array Values(1:100);

for Index := 1 step 1 until 100 do
    Sum := Sum + Values(Index);

배열의 하한과 상한은 LowerBoundUpperBound로 확인할 수 있다.

integer FirstIndex, LastIndex;

FirstIndex := LowerBound(Values, 1);
LastIndex := UpperBound(Values, 1);

두 번째 인자는 확인할 차원의 번호다.

참조 배열

객체 참조도 배열 요소로 사용할 수 있다.

ref(Customer) array Customers(1:100);

Customers(1) :- new Customer("First");
Customers(2) :- new Customer("Second");

서로 다른 하위 클래스 객체를 상위 클래스 참조 배열에 저장할 수 있다.

ref(Shape) array Shapes(1:3);

Shapes(1) :- new Circle(5.0);
Shapes(2) :- new Rectangle(4.0, 8.0);
Shapes(3) :- new Circle(2.0);

가상 절차를 사용하면 같은 반복문에서 각 객체의 실제 클래스에 맞는 동작을 호출할 수 있다.

integer Index;

for Index := 1 step 1 until 3 do
    Shapes(Index).Draw;

절차 선언

값을 반환하지 않는 절차는 procedure로 선언한다.

procedure PrintLine(Message);
    text Message;
begin
    OutText(Message);
    OutImage
end;

호출은 절차 이름 뒤에 인자를 작성한다.

PrintLine("Hello from Simula");

매개변수가 없는 짧은 절차는 하나의 문장으로 정의할 수 있다.

procedure NewLine;
    OutImage;

절차의 본문은 단일 문장이나 블록이 될 수 있다.

값을 반환하는 절차

반환 타입을 procedure 앞에 지정하면 함수와 같은 형식 절차가 된다.

real procedure Square(Value);
    value Value;
    real Value;
begin
    Square := Value * Value
end;

반환값은 절차 이름에 대입한다.

real Result;

Result := Square(5.0);

여러 경로에서 결과를 지정할 수도 있다.

Boolean procedure IsPositive(Value);
    value Value;
    real Value;
begin
    if Value > 0.0 then
        IsPositive := true
    else
        IsPositive := false
end;

절차 이름에 값을 대입하지 않고 종료되는 경로가 없도록 작성하는 편이 적합하다.

매개변수 명세

절차와 클래스의 매개변수는 머리 부분에서 이름을 나열한 뒤, 이어지는 명세에서 전달 방식과 타입을 지정한다.

real procedure Distance(X1, Y1, X2, Y2);
    value X1, Y1, X2, Y2;
    real X1, Y1, X2, Y2;
begin
    Distance :=
        Sqrt(
            (X2 - X1) ** 2 +
            (Y2 - Y1) ** 2
        )
end;

매개변수 목록과 타입 선언이 분리되어 있다는 점은 C, Java와 같은 현대 언어의 real X1 형태 매개변수 목록과 다르다.

클래스 매개변수도 같은 방식으로 선언한다.

class Point(X, Y);
    value X, Y;
    real X, Y;
begin
end;

값 호출과 이름 호출

Simula의 매개변수는 값 호출과 이름 호출을 지원한다.

value 명세가 있는 매개변수는 호출 시 계산된 값을 복사한다.

procedure AddOne(Number);
    value Number;
    integer Number;
begin
    Number := Number + 1
end;

호출자의 변수는 변경되지 않는다.

integer Value;

Value := 10;
AddOne(Value);

! Value는 여전히 10이다.;

name으로 지정된 매개변수는 호출자의 표현식이나 변수에 연결된다.

procedure Increment(Number);
    name Number;
    integer Number;
begin
    Number := Number + 1
end;
integer Value;

Value := 10;
Increment(Value);

! Value는 11이다.;

이름 호출은 호출 시점에 단순히 주소를 전달하는 참조 호출과 완전히 같지 않다. 실제 매개변수 표현식이 절차 안에서 사용될 때 호출자의 문맥에서 다시 평가될 수 있다.

다음은 이름 호출을 사용하는 합계 절차다.

real procedure Sigma(Index, First, Last, Expression);
    name Index, Expression;
    integer Index, First, Last;
    real Expression;
begin
    real Sum;

    Index := First;

    while Index <= Last do
    begin
        Sum := Sum + Expression;
        Index := Index + 1
    end;

    Sigma := Sum
end;
integer I;
real A, Result;

A := 2.0;

Result :=
    Sigma(
        I,
        1,
        100,
        1 / (I + A) ** 2
    );

Expression은 절차 안에서 사용될 때마다 현재 I를 사용해 다시 계산된다.

재귀 절차

절차는 자신을 다시 호출할 수 있다.

integer procedure Factorial(Number);
    value Number;
    integer Number;
begin
    if Number <= 1 then
        Factorial := 1
    else
        Factorial :=
            Number * Factorial(Number - 1)
end;
OutInt(Factorial(5), 5);
OutImage;

각 호출은 자신만의 매개변수와 지역 변수를 가진 동적 활성 레코드를 생성한다.

절차 매개변수

절차 자체를 다른 절차의 매개변수로 전달할 수 있다.

real procedure Apply(Function, Value);
    real procedure Function;
    value Value;
    real Value;
begin
    Apply := Function(Value)
end;

전달되는 절차의 결과 타입과 매개변수 형식은 호출 문맥과 호환되어야 한다. 형식 절차의 완전한 명세 방식은 구현별 지원 범위에 차이가 있을 수 있으며, GNU Cim은 명세되지 않은 형식 절차와 가상 절차 매개변수에 일부 제한이 있음을 밝히고 있다.[59]

클래스 선언

클래스는 class 키워드, 클래스 이름, 선택적인 매개변수와 클래스 본문으로 구성된다.

class Point(X, Y);
    value X, Y;
    real X, Y;
begin
    procedure Move(DX, DY);
        value DX, DY;
        real DX, DY;
    begin
        X := X + DX;
        Y := Y + DY
    end
end;

클래스 선언만으로 객체가 생성되지는 않는다. new 식이 실행될 때 클래스 인스턴스가 생성된다.

ref(Point) Position;

Position :- new Point(10.0, 20.0);
Position.Move(5.0, -2.0);

클래스 매개변수와 본문의 변수·절차는 객체의 속성이 된다.

객체 참조 선언

객체 참조 변수는 ref(클래스명) 형식으로 선언한다.

ref(Point) Position;
ref(Customer) CurrentCustomer;
ref(Vehicle) ActiveVehicle;

어떤 객체도 가리키지 않는 참조는 none이다.

Position :- none;

if Position == none then
    OutText("No position");

상위 클래스 참조는 하위 클래스 객체를 가리킬 수 있다.

ref(Vehicle) VehicleReference;
ref(Car) CarReference;

CarReference :- new Car;
VehicleReference :- CarReference;

반대 방향의 대입은 객체의 실제 클래스 확인이나 qua를 필요로 한다.

객체 생성

객체는 new 식으로 생성한다.

ref(Point) A, B;

A :- new Point(1.0, 2.0);
B :- new Point(10.0, 20.0);

new는 객체의 저장 공간을 만들고 클래스 매개변수를 연결한 뒤 클래스 본문을 실행하며, 생성된 객체의 참조를 결과로 반환한다.

객체 생성식을 다른 문장의 인자로 바로 사용할 수도 있다.

Register(new Customer("Kim"));

시뮬레이션 프로세스는 생성과 활성화가 분리된다.

ref(Customer) C;

C :- new Customer("Kim");
Activate C;

new는 프로세스 객체를 만들고, Activate는 사건 목록에 넣어 실행을 예약한다.

접두 클래스

상속은 기존 클래스 이름을 새 클래스 선언 앞에 작성하는 접두 구조로 표현한다.

class Vehicle;
begin
    real Position, Speed;

    procedure Move(Duration);
        value Duration;
        real Duration;
    begin
        Position := Position + Speed * Duration
    end
end;

Vehicle class Car(Seats);
    integer Seats;
begin
    Boolean EngineRunning
end;

현대적인 의사 문법으로 표현하면 Car extends Vehicle에 해당한다.

접두 구조는 여러 단계로 이어질 수 있다.

class Entity;
begin
    integer Identifier
end;

Entity class MovingEntity;
begin
    real X, Y
end;

MovingEntity class Aircraft;
begin
    real Altitude
end;

Simula는 하나의 직접 접두 클래스만 지정하는 단일 상속 언어다.

inner

접두 클래스 본문에서 inner는 실제 객체의 더 안쪽 하위 클래스 본문을 실행한다.

class Base;
begin
    OutText("Base before");
    OutImage;

    inner;

    OutText("Base after");
    OutImage
end;

Base class Derived;
begin
    OutText("Derived");
    OutImage
end;

new Derived를 실행하면 다음 순서로 출력된다.

Base before
Derived
Base after

inner를 작성하지 않으면 하위 클래스 본문의 실행 위치에 영향을 준다. 접두 클래스 설계자는 하위 클래스가 실행을 확장할 지점을 상위 클래스 본문 안에 명시할 수 있다.

가상 절차 선언

가상 속성은 클래스 머리 부분의 virtual: 명세에서 선언한다.

class Shape;
virtual:
    procedure Draw;
begin
    procedure Draw;
    begin
        OutText("Shape");
        OutImage
    end
end;

하위 클래스는 같은 이름의 절차를 선언해 가상 절차를 재정의한다.

Shape class Circle(Radius);
    real Radius;
begin
    procedure Draw;
    begin
        OutText("Circle radius: ");
        OutFix(Radius, 2, 8);
        OutImage
    end
end;

상위 클래스 참조를 통해 호출해도 실제 객체의 구현이 선택된다.

ref(Shape) Item;

Item :- new Circle(5.0);
Item.Draw;

가상 명세에는 절차의 형식 정보를 함께 작성할 수 있다. 가상 절차에 실제 구현이 제공되지 않은 상태에서 호출하면 런타임 오류가 발생할 수 있다.

this

현재 객체는 this 클래스명 형식으로 참조한다.

class Node;
begin
    ref(Node) Next;

    procedure Connect(Other);
        ref(Node) Other;
    begin
        this Node.Next :- Other
    end
end;

중첩 클래스에서는 외부 클래스 객체를 참조하는 데 사용할 수 있다.

class Container;
begin
    class Element;
    begin
        ref(Container) Owner;

        Owner :- this Container
    end
end;

현대 언어의 thisself와 달리 참조하려는 클래스 이름을 함께 지정한다.

원격 식별

객체의 속성에는 점 표기법으로 접근한다.

Position.X := 10.0;
Position.Y := 20.0;
Position.Move(5.0, 3.0);

표준 문서에서는 이러한 접근을 원격 식별 또는 remote identification이라고 한다.

점 왼쪽 참조가 none이면 유효한 객체 속성에 접근할 수 없으므로 런타임 오류가 발생한다.

if Position =/= none then
    Position.Move(1.0, 1.0);

inspect

inspect는 객체 참조를 검사하고 해당 객체의 속성을 한정자 없이 사용할 수 있는 문맥을 만든다.

inspect Position do
begin
    X := X + 1.0;
    Y := Y + 1.0;

    OutFix(X, 2, 8);
    OutFix(Y, 2, 8);
    OutImage
end;

참조가 none이면 do 문장은 실행되지 않는다. otherwise로 대체 동작을 지정할 수 있다.

inspect Position
do
    OutText("Position exists")
otherwise
    OutText("No position");

OutImage;

when을 사용하면 실제 객체의 클래스에 따라 분기할 수 있다.

inspect ShapeReference
when Circle do
begin
    OutText("Circle radius: ");
    OutFix(Radius, 2, 8);
    OutImage
end
when Rectangle do
begin
    OutText("Rectangle: ");
    OutFix(Width, 2, 8);
    OutText(" x ");
    OutFix(Height, 2, 8);
    OutImage
end
otherwise
begin
    OutText("Unknown shape");
    OutImage
end;

when 절 안에서는 해당 클래스에 선언된 속성을 직접 사용할 수 있다.

qua

qua는 객체 참조를 지정한 클래스 타입으로 한정한다.

ref(Vehicle) GeneralVehicle;
ref(Car) SpecificCar;

GeneralVehicle :- new Car(5);
SpecificCar :- GeneralVehicle qua Car;

객체가 지정한 클래스와 호환되지 않으면 런타임 오류가 발생한다.

(GeneralVehicle qua Car).EngineRunning := true;

실제 클래스가 불확실한 경우에는 먼저 inspect ... when으로 검사하는 방식이 적합하다.

hiddenprotected

후기 Simula 표준에서는 클래스 속성의 접근과 재정의를 제한하기 위해 hiddenprotected 명세를 제공한다.

class Account(InitialBalance);
    value InitialBalance;
    real InitialBalance;
hidden Balance;
begin
    real Balance;

    procedure Deposit(Amount);
        value Amount;
        real Amount;
    begin
        Balance := Balance + Amount
    end;

    real procedure GetBalance;
        GetBalance := Balance;

    Balance := InitialBalance
end;

hidden Balance는 외부에서 AccountReference.Balance 형식으로 직접 접근하는 것을 제한한다.

protected는 접두 클래스 계층에서 속성이 재정의되거나 외부에서 접근되는 방식을 제한한다. 두 명세의 세부 의미는 단순히 현대 언어의 privateprotected에 일대일로 대응하지 않으며, 가상 속성·접두 구조와 함께 표준의 가시성 규칙을 따른다.[60]

중첩 클래스

클래스 안에 다른 클래스를 선언할 수 있다.

class Graph;
begin
    integer NextIdentifier;

    class Vertex;
    begin
        integer Identifier;

        NextIdentifier := NextIdentifier + 1;
        Identifier := NextIdentifier
    end;

    ref(Vertex) FirstVertex;

    FirstVertex :- new Vertex
end;

Vertex 객체는 자신을 둘러싼 Graph 객체의 NextIdentifier에 접근할 수 있다.

내부 클래스는 외부 클래스의 정적 문맥에 속하므로, 일반적으로 적절한 외부 객체 문맥 안에서 생성해야 한다.

별도 컴파일

Simula는 클래스와 절차를 별도의 소스 단위로 컴파일하고 다른 프로그램에서 사용할 수 있는 구현 방식을 지원해 왔다. 구체적인 문법과 빌드 절차는 컴파일러에 따라 달라진다.

GNU Cim에서는 주 Simula 프로그램 하나와 다른 Simula 또는 C 모듈을 함께 컴파일할 수 있다. 주 프로그램이 있으면 필요한 런타임 및 Simula 모듈을 연결하며, 별도 클래스나 절차만 컴파일할 때는 연결 과정을 생략할 수 있다.[61]

cim program.sim

외부 C 함수와 연결하거나 컴파일러별 확장을 사용하는 소스는 표준 Simula 문법과 분리해 설명하는 것이 적합하다.

텍스트 선언과 프레임

text 값은 문자 저장 영역 전체 또는 일부를 나타내는 프레임으로 동작한다.

text FullText, Part;

FullText :- "SIMULA LANGUAGE";
Part :- FullText.Sub(1, 6);

OutText(Part);
OutImage;

Sub(Start, Length)는 같은 문자 저장 영역의 일부 구간을 나타내는 텍스트를 반환한다.

Blanks로 쓰기 가능한 텍스트 프레임을 만들 수 있다.

text Buffer;

Buffer :- Blanks(20);
Buffer := "Hello";

텍스트가 가리키는 문자 프레임의 길이는 Length로 확인한다.

integer Size;

Size := Buffer.Length;

현재 읽기 또는 쓰기 위치는 Pos로 확인하며, SetPos로 변경할 수 있다.

Buffer.SetPos(1);

while Buffer.More do
    OutChar(Buffer.GetChar);

텍스트 결합

텍스트는 & 연산자로 결합할 수 있다.

text FirstName, LastName, FullName;

FirstName :- "Ole-Johan";
LastName :- "Dahl";

FullName :- FirstName & " " & LastName;

OutText(FullName);
OutImage;

결합 결과는 두 텍스트의 문자를 순서대로 포함하는 새 텍스트를 나타낸다.

숫자를 텍스트로 바꾸는 구체적인 표준 절차와 구현 확장은 컴파일러에 따라 차이가 있을 수 있으므로, 이식 가능한 코드에서는 출력 절차를 직접 조합하는 방식도 사용할 수 있다.

텍스트 비교

텍스트 값은 사전식 순서에 따라 비교할 수 있다.

text First, Second;

First :- "ABC";
Second :- "ABD";

if First < Second then
    OutText("First comes before Second");

텍스트 참조가 같은 프레임을 가리키는지와 문자의 내용이 같은지는 서로 다른 개념이다. 문자의 값 비교와 참조 대입을 구분해야 한다.

기본 출력

OutText는 텍스트를 현재 출력 이미지에 기록한다.

OutText("Hello, Simula");
OutImage;

OutImage는 현재 출력 이미지를 완료하고 다음 줄로 이동한다.

문자 하나는 OutChar로 출력한다.

OutChar('A');
OutImage;

정수는 OutInt로 출력한다.

integer Count;

Count := 42;

OutInt(Count, 5);
OutImage;

두 번째 인자는 출력 필드의 너비다.

실수 출력에는 OutFix, OutRealOutFrac 등을 사용할 수 있다.

real Value;

Value := 12.3456;

OutFix(Value, 2, 10);
OutImage;

OutFix(Value, 2, 10)은 소수점 아래 두 자리와 전체 필드 너비 10을 사용해 출력한다.

기본 입력

InInt는 입력에서 정수를 읽는다.

integer Count;

Count := InInt;

OutText("Count: ");
OutInt(Count, 5);
OutImage;

실수는 InReal로 읽는다.

real Value;

Value := InReal;

문자 하나는 InChar로 읽을 수 있다.

character Command;

Command := InChar;

현재 입력 이미지의 다음 줄로 이동하거나 새 이미지를 불러오는 처리는 InImage와 입력 파일 클래스의 상태를 사용한다.

파일 입출력

Simula의 파일 입출력은 File 계열 시스템 클래스를 사용한다. 텍스트 입력에는 InFile, 출력에는 OutFile을 사용할 수 있다.

ref(OutFile) Output;

Output :- new OutFile("result.txt");

if Output.Open(Blanks(80)) then
begin
    Output.OutText("Simulation result");
    Output.OutImage;
    Output.Close
end;

입력 파일도 객체로 생성하고 연다.

ref(InFile) Input;
integer Value;

Input :- new InFile("data.txt");

if Input.Open(Blanks(80)) then
begin
    Value := Input.InInt;
    Input.Close
end;

파일 클래스의 정확한 생성자와 Open 인자는 표준 환경 및 구현 설명서를 함께 확인해야 한다. 순차 파일, 직접 파일, 바이트 파일과 프린트 파일 등 여러 시스템 클래스가 제공될 수 있다.

표준 수학 절차

Simula 환경은 일반적인 수학 함수를 제공한다.

real X, Result;

X := 2.0;

Result := Sqrt(X);
Result := Sin(X);
Result := Cos(X);
Result := Exp(X);
Result := Ln(X);

주요 절차에는 다음과 같은 것들이 있다.

절차기능
Abs절댓값
Sign부호
Sqrt제곱근
Sin사인
Cos코사인
Tan탄젠트
Arctan아크탄젠트
Exp지수 함수
Ln자연로그
Entier인자보다 크지 않은 최대 정수
Addepsilon구현 정밀도에 따른 작은 값 추가
Subepsilon구현 정밀도에 따른 작은 값 감소

함수의 인자 범위와 오류 처리는 표준 및 구현의 수학 라이브러리 규칙을 따른다.

난수와 분포 절차

Simula 환경은 시뮬레이션을 위한 난수 및 확률 분포 절차를 제공한다.

integer Seed;
real RandomValue;

Seed := 12345;
RandomValue := Draw(0.5, Seed);

주요 절차에는 균등 분포, 정규 분포, 지수 분포와 포아송 분포 등을 위한 기능이 포함된다.

real UniformValue, NormalValue, Interval;

UniformValue := Uniform(0.0, 1.0, Seed);
NormalValue := Normal(10.0, 2.0, Seed);
Interval := NegExp(5.0, Seed);

난수 상태를 나타내는 정수 인자는 호출 과정에서 갱신된다. 같은 초기값과 동일한 호출 순서를 사용하면 실행을 재현할 수 있다.

코루틴 제어

일반 클래스 객체의 실행을 중단하고 재개하는 데 detach, callresume을 사용할 수 있다.

class Worker;
begin
    integer Step;

    for Step := 1 step 1 until 3 do
    begin
        OutInt(Step, 3);
        OutImage;

        detach
    end
end;

detach는 객체의 실행 위치를 보존하고 제어권을 해당 객체를 생성하거나 호출한 문맥으로 돌려준다.

ref(Worker) W;

W :- new Worker;

resume(W);
resume(W);
resume(W);

call은 호출자와 피호출 객체 사이의 코루틴 연결을 만들고, resume은 준병렬 시스템에서 실행 중인 객체를 교체한다. 이 문법의 유효성은 객체의 현재 상태와 동적 연결 관계에 따라 결정된다.

시뮬레이션 문법

시뮬레이션 프로그램은 주 블록에 Simulation 접두부를 지정한다.

Simulation
begin
    Process class Job;
    begin
        Hold(5.0)
    end;

    ref(Job) J;

    J :- new Job;
    Activate J;

    Hold(10.0)
end;

Process classSimulation 내부의 Process 클래스를 접두 클래스로 사용하는 선언이다.

프로세스 활성화에는 여러 문장 형태가 있다.

Activate P;
Activate P delay 5.0;
Activate P delay 5.0 prior;
Activate P at 20.0;
Activate P at 20.0 prior;
Activate P before Q;
Activate P after Q;

기존 예약을 변경하려면 Reactivate를 사용한다.

Reactivate P at Time + 1.0 prior;

현재 프로세스의 실행을 일정 시간 미룬다.

Hold(10.0);

현재 프로세스를 사건 목록에서 제거한다.

Passivate;

현재 프로세스를 대기열에 넣는다.

Wait(WaitingQueue);

다른 프로세스의 예약을 취소한다.

Cancel(P);

완전한 기본 예제

다음 프로그램은 변수, 반복문, 절차, 클래스, 객체 생성과 출력 문법을 함께 사용한다.

begin
    class Counter(Name, InitialValue);
        text Name;
        value InitialValue;
        integer InitialValue;
    begin
        integer Value;

        procedure Increment;
            Value := Value + 1;

        procedure Print;
        begin
            OutText(Name);
            OutText(": ");
            OutInt(Value, 4);
            OutImage
        end;

        Value := InitialValue
    end;

    ref(Counter) First, Second;
    integer Index;

    First :- new Counter("First", 0);
    Second :- new Counter("Second", 100);

    for Index := 1 step 1 until 5 do
        First.Increment;

    for Index := 1 step 1 until 3 do
        Second.Increment;

    First.Print;
    Second.Print
end;

실행 결과는 다음과 같다.

First:    5
Second: 103

상속과 가상 절차 예제

다음 프로그램은 접두 클래스와 가상 절차를 이용해 서로 다른 객체를 공통 타입으로 처리한다.

begin
    class Shape;
    virtual:
        procedure Print;
    begin
        procedure Print;
        begin
            OutText("Shape");
            OutImage
        end
    end;

    Shape class Circle(Radius);
        value Radius;
        real Radius;
    begin
        procedure Print;
        begin
            OutText("Circle, radius = ");
            OutFix(Radius, 2, 8);
            OutImage
        end
    end;

    Shape class Rectangle(Width, Height);
        value Width, Height;
        real Width, Height;
    begin
        procedure Print;
        begin
            OutText("Rectangle, ");
            OutFix(Width, 2, 8);
            OutText(" x ");
            OutFix(Height, 2, 8);
            OutImage
        end
    end;

    ref(Shape) array Shapes(1:3);
    integer Index;

    Shapes(1) :- new Circle(5.0);
    Shapes(2) :- new Rectangle(4.0, 8.0);
    Shapes(3) :- new Circle(2.5);

    for Index := 1 step 1 until 3 do
        Shapes(Index).Print
end;

실행 결과는 다음과 같다.

Circle, radius =     5.00
Rectangle,     4.00 x     8.00
Circle, radius =     2.50

문법적 특징

Simula 문법은 현대의 C 계열 객체 지향 언어와 비교하면 다음과 같은 차이를 가진다.

기능Simula 문법현대 언어의 일반적 문법
블록begin ... end{ ... } 또는 들여쓰기
값 대입:==
객체 참조 대입:-주로 =
같지 않음<>!=
참조 동일성==, =/===, is, 참조 비교
거듭제곱**함수 또는 **
정수 나눗셈///, div, //
클래스 선언class C; begin ... endclass C { ... }
상속Base class DerivedDerived extends Base
객체 참조ref(C)C, C*, 참조 타입
객체 생성new C(...)new C(...)
현재 객체this Cthis, self
형식 한정Object qua C캐스트
타입 분기inspect ... when패턴 매칭, 타입 검사
가상 절차virtual: 명세virtual, override
상속 본문 확장innersuper, 생성자 연결
주석! ... ;//, /* ... */
반복문for I := 1 step 1 until Nfor, 범위 반복
줄 출력OutText, OutImageprint, 스트림

Simula는 문장 끝마다 세미콜론을 무조건 요구하는 문법이 아니다. 세미콜론은 문장 종료 기호라기보다 문장 사이의 구분자에 가깝기 때문에, end 바로 앞의 마지막 문장에는 생략되는 경우가 많다.

begin
    Count := 1;
    Total := 10.0;
    OutText("Done")
end;

다만 선언과 중첩 구조가 복잡한 코드에서는 문장 경계를 분명히 하기 위해 일관된 위치에 세미콜론을 사용하는 편이 읽기 쉽다.

Simula의 문법은 클래스와 객체 기능을 ALGOL의 기존 선언·블록 문법에 통합한다. 클래스는 특수한 별도 파일 구조가 아니라 이름이 있는 블록 패턴으로 선언되고, 접두 클래스는 기존 블록 앞에 다른 클래스의 문맥을 결합한다. 가상 절차, inner, inspectqua도 이 블록 및 참조 모델을 중심으로 정의된다.

따라서 Simula 문법을 이해하려면 키워드의 표면적인 형태뿐 아니라 블록의 정적 범위, 실행 시 생성되는 동적 객체, 값과 참조의 구분, 접두 클래스의 실행 순서와 객체의 실제 클래스에 따른 가상 바인딩을 함께 이해해야 한다.

타입 체계와 메모리 관리

Simula는 변수, 절차 결과, 매개변수와 배열 요소의 타입을 선언하는 정적 타입 언어이다. 산술값과 논리값 같은 값 타입, 클래스 계층으로 한정되는 객체 참조 타입, 문자와 텍스트, 배열, 절차·레이블·스위치와 같은 여러 종류의 언어 요소를 구분한다. 대부분의 타입 호환성은 컴파일 과정에서 검사되지만, 상위 클래스 참조를 하위 클래스 참조로 좁히는 연산처럼 실제 객체의 클래스에 의존하는 일부 검사는 실행 중 수행된다.

Simula의 타입 체계는 클래스 이름과 접두 클래스 계층을 기준으로 객체 참조의 호환성을 결정한다. 따라서 서로 같은 속성과 절차를 가진 클래스라도 동일한 클래스 계층에 속하지 않으면 호환되는 객체 타입으로 취급되지 않는다. 이러한 방식은 현대 용어로 명목적 타입에 해당한다.

메모리 관리는 일반 블록의 동적 인스턴스, new로 생성되는 클래스 객체, 배열과 텍스트 객체의 수명을 서로 구분한다. 언어 사양은 구체적인 스택·힙 배치를 강제하지 않으며, 실행 중 생성되는 블록 인스턴스의 포함 관계와 객체 참조 가능성을 중심으로 수명을 정의한다. 구현체는 더 이상 접근할 수 없는 객체를 가비지 컬렉션으로 회수할 수 있다.[62]

타입의 분류

Simula의 타입은 크게 값 자체를 저장하는 값 타입과 다른 동적 대상에 대한 참조를 저장하는 참조 타입으로 나눌 수 있다.

분류주요 타입의미
산술 타입short integer, integer, real, long real정수와 실수값
논리 타입Booleantrue 또는 false
문자 타입character구현 문자 집합의 문자 하나
텍스트 타입text문자 프레임을 나타내는 텍스트 참조
객체 참조 타입ref(C)클래스 C 또는 허용되는 하위 클래스 객체의 참조
배열T array일정한 타입의 요소와 인덱스 범위를 가진 배열
절차 관련 타입타입이 지정된 절차, procedure호출 가능한 절차와 절차 결과
제어 관련 타입label, switch프로그램 위치와 레이블 선택 구조
begin
    short integer SmallCount;
    integer Count;
    real Distance;
    long real SimulationTime;
    Boolean Finished;
    character Command;
    text Message;

    Count := 100;
    Distance := 12.5;
    SimulationTime := 1000000.0;
    Finished := false;
    Command := 'A';
    Message :- "Simula"
end;

textref(C)는 모두 참조적 의미를 가지지만 같은 종류의 타입은 아니다. text는 표준이 별도로 정의하는 문자 프레임과 위치 표시자를 가지며, 객체 참조는 특정 클래스 계층의 인스턴스를 가리킨다.

정적 타입 검사

변수와 절차 결과의 타입은 선언으로 정해진다.

integer Count;
real Average;
Boolean Available;
ref(Customer) CurrentCustomer;

대입식의 오른쪽 값이나 참조는 원칙적으로 왼쪽 타입과 일치해야 한다. 허용되는 산술 변환과 클래스 계층에 따른 참조 호환성은 별도의 규칙으로 처리된다. Common Base Language는 대입할 값 또는 참조의 타입이 왼쪽 타입과 일치해야 한다는 원칙을 두고, 산술 대입과 객체 참조 대입에 대한 예외를 따로 규정한다.[63]

다음과 같은 대입은 각 타입의 규칙에 따라 검사된다.

integer Count;
real Measurement;
Boolean Ready;

Count := 10;
Measurement := 15.5;
Ready := Count > 0;

논리식의 결과는 Boolean이며 산술 변수에 직접 대입할 수 없다.

integer Count;
Boolean Ready;

! 타입이 서로 다르므로 허용되지 않는다.;
! Count := Ready;

Simula를 단순히 ‘강한 타입’이나 ‘약한 타입’ 가운데 하나로만 분류하면 일부 특성이 가려진다. 타입은 정적으로 선언되고 서로 관계없는 객체 참조 타입의 대입은 금지되지만, 정수와 실수 사이에는 자동 산술 변환이 있으며 일부 하향 참조 대입은 실행 중 검사를 거친다.

산술 타입

산술 타입은 정수 계열과 실수 계열로 구성된다.

short integer SmallValue;
integer Count;
real Position;
long real PreciseTime;

integerreal 사이의 대입에는 필요한 산술 변환이 적용될 수 있다.

integer Whole;
real Fractional;

Whole := 3.75;
Fractional := 10;

Common Base Language는 산술값을 real 또는 integer 왼쪽 부분에 대입할 수 있으며 필요하면 적절한 변환 함수를 적용한다고 규정한다. 실수를 정수에 대입할 때 어떤 정수값이 선택되는지는 Simula의 정수 변환 규칙에 따른다.[64]

연쇄 대입에서는 각 왼쪽 타입에 맞는 변환이 단계별로 일어날 수 있다.

integer I;
real X, Y;

X := I := Y := 3.14;

이 경우 Y에는 실수값이 저장되고 I에는 정수로 변환된 값이 저장된다. 가장 바깥쪽 X에는 중간 대입 결과와 규칙에 따라 값이 전달된다.

산술 표현식의 타입

산술 표현식의 결과 타입은 피연산자의 타입과 연산자에 의해 결정된다.

integer A, B, IntegerResult;
real RealResult;

A := 7;
B := 2;

IntegerResult := A // B;
RealResult := A / B;

//는 정수 나눗셈이며 /는 일반 산술 나눗셈에 사용된다. 거듭제곱 **의 결과 타입도 밑과 지수의 타입 조합에 따라 결정된다.

integer IntegerPower;
real RealPower;

IntegerPower := 2 ** 8;
RealPower := 2.5 ** 3;

산술 표현식이 최종 변수에 대입될 때에는 다시 왼쪽 타입에 따른 변환 규칙이 적용될 수 있다.

Boolean 타입

Booleantruefalse 가운데 하나의 값을 가진다.

Boolean Open, Valid;

Open := true;
Valid := Open and Count > 0;

관계 연산과 참조 비교도 Boolean 값을 생성한다.

Boolean SameObject, Positive;

Positive := Count > 0;
SameObject := First == Second;

Boolean은 산술 정수와 구분되는 독립 타입이다. 참과 거짓을 1과 0으로 암묵적으로 대체하는 방식은 표준 타입 모델에 포함되지 않는다.

문자 타입

character는 구현이 제공하는 내부 문자 집합의 문자 하나를 나타낸다.

character Letter;

Letter := 'A';

문자 순서는 구현이 정의하는 collating sequence에 의해 정해진다. 표준 절차 Rank는 문자를 해당 순서의 정수값으로 변환하고, Char는 정수값을 문자로 변환한다.[65]

integer Code;
character Letter;

Code := Rank('A');
Letter := Char(Code);

따라서 문자 정렬 순서의 구체적인 값이 모든 구현에서 같은 숫자라고 가정하기보다 RankChar를 사용하는 편이 이식성에 적합하다.

텍스트 타입

Simula의 text는 현대 언어의 불변 문자열 값과 완전히 같은 구조가 아니다. 텍스트 참조는 문자 저장 영역 안의 일정한 구간인 프레임과 현재 읽기·쓰기 위치를 나타내는 위치 표시자를 가진다.

text FullText, Part;

FullText :- "SIMULA LANGUAGE";
Part :- FullText.Sub(1, 6);

Part는 전체 문자를 복사한 독립 문자열이 아니라 FullText가 나타내는 문자 영역 중 일부를 가리킬 수 있다. Sub로 생성된 텍스트 참조에는 자체적인 프레임 길이와 위치 표시자가 있다.

Part.SetPos(1);

while Part.More do
    OutChar(Part.GetChar);

OutImage;

텍스트가 아무 문자 프레임도 나타내지 않는 값은 notext다.

text CurrentText;

CurrentText :- notext;

if CurrentText == notext then
    OutText("No text");

notext.length는 0이며, notext는 일반 객체 참조의 none과 비슷한 역할을 하지만 서로 다른 타입의 값이다.

텍스트 값과 텍스트 참조

텍스트에는 참조 대입값 대입이 모두 존재하며 두 연산의 의미가 다르다.

text Source, Alias, Copy;

Source :- "SIMULA";
Alias :- Source;

Copy :- Blanks(6);
Copy := Source;

Alias :- SourceSource가 나타내는 텍스트 프레임의 참조를 복사한다. 위치 표시자도 참조 대입 시 복사된다. Copy := Source는 Source의 문자값을 Copy가 이미 가리키는 프레임 안으로 복사한다.[66]

같은 문자 내용을 가진 두 텍스트가 물리적으로 서로 다른 문자 영역을 가리킬 수도 있다.

text A, B;

A :- "ABC";
B :- Blanks(3);
B := A;

if A = B then
    OutText("Equal character values");

OutImage;

if A =/= B then
    OutText("Different text references");

OutImage;

A = BA =/= B가 동시에 참일 수 있다. 첫 비교는 문자값이 같은지를, 두 번째 비교는 서로 다른 텍스트 객체 또는 프레임 참조인지를 나타낸다. 표준은 ===/=를 참조 비교로 정의하고 일반 관계 연산과 구분한다.[67]

배열 타입

배열은 요소 타입과 각 차원의 인덱스 범위로 구성된다.

integer array Counts(1:10);
real array Matrix(1:3, -2:2);
ref(Customer) array Customers(1:100);

Counts, MatrixCustomers는 서로 다른 배열 타입과 구조를 가진다. 배열 요소의 타입은 선언 시 정해지며, 요소 대입에도 해당 타입의 규칙이 적용된다.

Counts(1) := 10;
Matrix(1, -2) := 3.5;
Customers(1) :- new Customer("A");

객체 참조 배열은 선언된 클래스 또는 호환되는 하위 클래스 객체를 저장할 수 있다.

ref(Shape) array Shapes(1:3);

Shapes(1) :- new Circle(5.0);
Shapes(2) :- new Rectangle(4.0, 8.0);

배열 자체는 객체 참조와 다른 종류의 언어 요소다. 배열 매개변수는 ALGOL 계열의 배열 전달 규칙을 따르며, 배열의 실제 저장 영역과 수명은 배열을 참조하는 활성 구조와 관련된다.

절차의 타입

Simula 절차는 결과를 반환하지 않는 일반 절차와 타입이 지정된 형식 절차로 나뉜다.

procedure PrintLine(Message);
    text Message;
begin
    OutText(Message);
    OutImage
end;

real procedure Square(Value);
    value Value;
    real Value;
begin
    Square := Value * Value
end;

Squarereal 결과 타입을 가지며 절차 이름에 결과값을 대입한다.

real Result;

Result := Square(4.0);

결과 타입이 없는 proper procedure는 표준에서 universal 타입과 관련된 별도의 형식 규칙으로 설명된다. 절차를 매개변수로 전달할 때는 절차 종류, 결과 타입과 매개변수 명세가 호환되어야 한다.

매개변수 타입과 전달 방식

매개변수 선언은 이름 목록, 타입 명세와 전달 방식 명세로 나뉜다.

real procedure Distance(X1, Y1, X2, Y2);
    value X1, Y1, X2, Y2;
    real X1, Y1, X2, Y2;
begin
    Distance :=
        Sqrt(
            (X2 - X1) ** 2 +
            (Y2 - Y1) ** 2
        )
end;

산술값과 Boolean 등의 값 타입 매개변수에는 value 또는 name 전달 방식을 사용할 수 있다. 객체 참조 매개변수는 지역 참조 변수처럼 초기화되며, 객체 자체가 복제되는 것은 아니다.

procedure Rename(PersonReference, NewName);
    ref(Person) PersonReference;
    text NewName;
begin
    PersonReference.Name := NewName
end;

PersonReference는 호출 시 전달된 객체의 참조를 가리킨다. 절차 안에서 객체 속성을 변경하면 호출자도 같은 객체를 통해 변경을 관찰한다.

ref(Person) P;

P :- new Person("Before");
Rename(P, "After");

OutText(P.Name);
OutImage;

참조 매개변수 변수 자체에 다른 참조를 대입하는 것과 참조 대상 객체의 속성을 변경하는 것은 구분된다.

객체 참조 타입

클래스 객체는 ref(C) 타입의 참조를 통해 사용한다.

class Vehicle;
begin
    real Speed
end;

ref(Vehicle) CurrentVehicle;

CurrentVehicle :- new Vehicle;

ref(Vehicle)Vehicle 클래스에 의해 한정된 객체 참조 타입이다. 아무 객체도 가리키지 않는 값은 none이다.

CurrentVehicle :- none;

if CurrentVehicle == none then
    OutText("No vehicle");

객체는 값 대입으로 통째로 복사할 수 없다. Common Base Language는 객체에 대한 값 대입 연산이 없다고 명시하며, 객체 사이의 연결은 참조 대입 :-로 구성한다.[68]

ref(Vehicle) First, Second;

First :- new Vehicle;
Second :- First;

Second :- First는 새 객체를 생성하거나 내용을 복사하지 않는다. 두 변수는 동일한 객체를 가리킨다.

클래스에 의한 참조 한정

각 객체 참조 표현식은 특정 클래스에 의해 한정된다. ref(Base)Base 클래스 수준에서 사용할 수 있는 속성과 허용되는 객체 집합을 정한다.

class Entity;
begin
    integer Identifier
end;

Entity class Vehicle;
begin
    real Speed
end;

Vehicle class Car;
begin
    integer Seats
end;
ref(Entity) AnyEntity;
ref(Vehicle) AnyVehicle;
ref(Car) SpecificCar;

SpecificCar :- new Car;
AnyVehicle :- SpecificCar;
AnyEntity :- SpecificCar;

Car 객체는 VehicleEntity의 속성을 포함하므로 상위 클래스 참조에 대입할 수 있다.

AnyEntity.Identifier := 1;
AnyVehicle.Speed := 20.0;
SpecificCar.Seats := 5;

AnyEntity가 실제로 Car를 가리키더라도 정적 한정이 Entity이므로 SpeedSeats에는 직접 접근할 수 없다.

상향 참조 대입

하위 클래스 객체의 참조를 상위 클래스 참조에 대입하는 것은 허용된다.

ref(Vehicle) VehicleReference;
ref(Car) CarReference;

CarReference :- new Car;
VehicleReference :- CarReference;

이 대입은 객체의 실제 구조를 바꾸지 않는다. VehicleReferenceCarReference는 동일한 Car 객체를 서로 다른 정적 한정으로 가리킨다.

if VehicleReference == CarReference then
    OutText("Same object");

OutImage;

상위 클래스 참조를 통한 접근은 상위 클래스 수준의 속성으로 제한되지만, 가상 절차 호출은 실제 객체의 가장 구체적인 구현을 선택할 수 있다.

하향 참조 대입

상위 클래스 참조에서 하위 클래스 참조로 대입하는 경우에는 실제 객체가 대상 하위 클래스와 호환되는지 확인해야 한다.

ref(Vehicle) GeneralVehicle;
ref(Car) CarReference;

GeneralVehicle :- new Car;
CarReference :- GeneralVehicle;

Simula 표준의 객체 참조 대입 규칙은 왼쪽 클래스가 오른쪽 정적 클래스보다 더 안쪽에 있는 경우에도 문법상 대입을 허용할 수 있지만, 실제 값이 none이거나 왼쪽 클래스 또는 그 하위 클래스의 객체인지 실행 중 검사한다. 조건을 충족하지 못하면 런타임 오류가 발생한다.[69]

ref(Vehicle) GeneralVehicle;
ref(Car) CarReference;

GeneralVehicle :- new Vehicle;

! 실제 객체가 Car가 아니므로 실행 오류가 발생한다.;
! CarReference :- GeneralVehicle;

따라서 Simula의 참조 타입 검사는 완전히 정적이지 않다. 클래스 계층으로 불가능한 관계는 번역 단계에서 거부되고, 정적으로 가능하지만 실제 객체에 따라 달라지는 하향 대입은 실행 중 확인될 수 있다.

qua에 의한 타입 한정

qua는 객체 참조 표현식을 특정 클래스 수준으로 명시적으로 한정한다.

ref(Vehicle) GeneralVehicle;
ref(Car) SpecificCar;

GeneralVehicle :- new Car;
SpecificCar :- GeneralVehicle qua Car;

GeneralVehicle qua Car는 참조 대상이 Car 또는 그 하위 클래스 객체인지 검사한다. 조건이 맞으면 결과는 Car에 의해 한정된 참조가 되고, 맞지 않으면 런타임 오류가 발생한다.

(GeneralVehicle qua Car).Seats := 4;

qua는 타입을 변경한 새 객체를 만드는 연산이 아니다. 같은 객체를 더 구체적인 클래스 수준에서 바라보는 참조 표현식을 만든다.

inspect와 타입 검사

실제 객체의 클래스가 불확실하면 inspect ... when으로 검사하면서 해당 타입의 속성에 접근할 수 있다.

inspect GeneralVehicle
when Car do
begin
    OutText("Car seats: ");
    OutInt(Seats, 3);
    OutImage
end
when Bus do
begin
    OutText("Bus capacity: ");
    OutInt(Capacity, 3);
    OutImage
end
otherwise
begin
    OutText("Another vehicle type");
    OutImage
end;

when 절은 객체가 해당 클래스에 속하는 경우에만 실행된다. 선택된 절 안에서는 검사 대상 객체가 해당 클래스에 맞게 한정되므로 하위 클래스 속성을 직접 사용할 수 있다.

inspect는 실행 중 타입 검사와 제한된 범위의 타입 한정을 함께 수행한다. 현대 언어의 타입 패턴 매칭과 비슷한 역할을 하지만 Simula의 연결문 및 원격 접근 규칙 안에서 정의된다.

isin

Simula는 객체와 클래스의 관계를 검사하는 Boolean 관계를 제공한다.

if ObjectReference is Car then
    OutText("Exactly a Car");

if ObjectReference in Vehicle then
    OutText("Vehicle or a subclass");

X is C는 객체가 정확히 클래스 C에 속하는지를 검사한다. X in C는 객체가 C 또는 C보다 안쪽의 하위 클래스에 속하는지를 검사한다.[70]

ref(Vehicle) V;

V :- new Car;

if V is Car then
    OutText("The exact class is Car");

OutImage;

if V in Vehicle then
    OutText("The object belongs to the Vehicle hierarchy");

OutImage;

이 관계들은 객체를 변환하지 않으며 검사 결과만 반환한다.

참조 동일성과 값 동일성

객체 참조의 비교에는 ===/=를 사용한다.

ref(Point) A, B, C;

A :- new Point(1.0, 2.0);
B :- A;
C :- new Point(1.0, 2.0);

if A == B then
    OutText("A and B denote the same object");

OutImage;

if A =/= C then
    OutText("A and C denote different objects");

OutImage;

AC의 속성값은 같지만 다른 new 식으로 생성되었으므로 서로 다른 객체다. Simula의 기본 객체 비교는 객체의 구조적 동등성을 자동으로 계산하지 않는다. 내용 비교가 필요하면 클래스에 비교 절차를 정의해야 한다.

class Point(X, Y);
    real X, Y;
begin
    Boolean procedure Equals(Other);
        ref(Point) Other;
    begin
        Equals :=
            Other =/= none and then
            X = Other.X and then
            Y = Other.Y
    end
end;

객체의 정체성과 객체가 가진 데이터의 동등성을 분리하는 구조다.

명목적 클래스 타입

클래스 참조 호환성은 속성 구조가 아니라 선언된 접두 클래스 관계로 정해진다.

class FirstPoint;
begin
    real X, Y
end;

class SecondPoint;
begin
    real X, Y
end;

두 클래스가 같은 이름과 타입의 X, Y를 갖더라도 서로 같은 계층에 속하지 않는다.

ref(FirstPoint) A;
ref(SecondPoint) B;

A :- new FirstPoint;
B :- new SecondPoint;

! 구조가 같아도 서로 관련 없는 참조 타입이다.;
! A :- B;

반면 명시적으로 접두 관계를 정의하면 참조 호환성이 생긴다.

FirstPoint class ColoredPoint;
begin
    integer Color
end;

ref(FirstPoint) PointReference;
PointReference :- new ColoredPoint;

이는 Simula의 타입 체계가 클래스 선언의 이름과 계보를 중심으로 구성된다는 점을 보여준다.

가상 절차와 동적 타입

참조 표현식의 정적 타입은 접근 가능한 속성을 제한하지만, 가상 절차 호출은 객체의 실제 클래스를 사용해 구현을 선택한다.

class Shape;
virtual:
    procedure Draw;
begin
    procedure Draw;
        OutText("Shape")
end;

Shape class Circle;
begin
    procedure Draw;
        OutText("Circle")
end;
ref(Shape) S;

S :- new Circle;
S.Draw;

S의 정적 참조 타입은 Shape지만 실제 객체는 Circle이므로 Circle.Draw가 실행된다.

따라서 Simula의 객체 타입에는 두 관점이 함께 존재한다.

  • 정적 한정은 번역 시 접근 가능한 클래스 속성을 결정한다.
  • 동적 클래스는 가상 절차 호출과 실행 중 타입 검사에 사용된다.

이 구분은 이후 클래스 기반 객체 지향 언어의 정적 타입과 동적 디스패치 구조에 이어졌다.

블록 인스턴스와 저장 공간

Simula 프로그램이 블록에 진입하면 해당 블록의 동적 인스턴스가 생성된다. 인스턴스에는 지역 변수, 절차 실행에 필요한 문맥과 제어 상태가 포함된다.

begin
    integer OuterValue;

    procedure Calculate;
    begin
        integer LocalValue;

        LocalValue := OuterValue * 2
    end;

    OuterValue := 10;
    Calculate
end;

Calculate를 호출할 때마다 절차 본문의 새 동적 인스턴스가 만들어지며 LocalValue도 호출마다 독립적으로 존재한다. 일반적인 하위 블록과 절차 본문은 실행이 끝나면 삭제될 수 있다.

표준은 이러한 구조를 추상적인 block instance와 동적 포함 관계로 정의한다. 실제 구현이 이를 기계 스택, 힙, 디스플레이, 정적 링크 또는 다른 구조로 배치하는지는 구현 세부 사항이다.

클래스 객체의 생성

클래스 객체는 new 식으로 생성된다.

ref(Customer) First, Second;

First :- new Customer("First");
Second :- new Customer("Second");

각 객체는 다음 정보를 포함할 수 있다.

  • 클래스 매개변수와 지역 변수의 저장 공간
  • 클래스 내부 배열과 참조
  • 객체의 클래스 및 접두 클래스 정보
  • 가상 절차 바인딩에 필요한 정보
  • 정적으로 둘러싼 외부 블록 또는 객체의 문맥
  • 중단 가능한 객체라면 현재 실행 위치와 제어 상태

정확한 메모리 배치는 구현에 따라 다르다. Simula 표준은 객체가 어떤 주소에 놓이는지나 객체 머리말의 형식을 규정하지 않고, 프로그램에서 관찰되는 객체 참조와 수명 의미만 정의한다.

객체의 실행 상태와 수명

클래스 객체는 실행 상태에 따라 attached, detached와 terminated 상태를 가질 수 있다.

class Worker;
begin
    OutText("First step");
    OutImage;

    detach;

    OutText("Second step");
    OutImage
end;

객체는 생성 직후 자신을 생성한 블록 인스턴스에 attached 상태로 실행된다. detach를 실행하면 현재 위치를 보존한 채 detached 상태가 된다. 클래스 본문의 끝에 도달하면 terminated 상태가 된다.[71]

실행이 종료되었다고 해서 객체의 저장 공간이 즉시 사라지는 것은 아니다.

class Counter;
begin
    integer Value;

    procedure Increment;
        Value := Value + 1;

    Value := 10
end;

ref(Counter) C;

C :- new Counter;
C.Increment;

Counter의 클래스 본문은 생성 중 끝까지 실행되어 terminated 상태가 되지만, C가 객체를 계속 참조하므로 ValueIncrement에는 접근할 수 있다. 실행 상태의 종료와 객체 저장 공간의 회수는 서로 다른 개념이다.

정적 범위와 객체 수명

클래스 선언은 특정 블록의 정적 범위 안에 존재한다. 해당 클래스 객체의 동적 수명은 클래스 선언을 포함하는 블록 인스턴스의 수명에 의해 상한이 정해진다.

begin
    class LocalObject;
    begin
        integer Value
    end;

    ref(LocalObject) Item;

    Item :- new LocalObject
end;

LocalObject 클래스는 최외곽 예제 블록에 지역적이다. 이 블록 인스턴스가 삭제될 때 그 안에 포함되는 동적 객체들도 더 이상 유효한 계산 가능한 참조로 접근될 수 없어야 한다.

Common Base Language는 블록 인스턴스가 삭제되면 그 인스턴스가 포함하는 다른 블록 인스턴스도 삭제되며, 객체가 삭제되는 시점에는 어떤 계산 가능한 객체 참조 표현식도 그 객체를 가리킬 수 없다고 규정한다. 따라서 객체의 동적 범위는 클래스 선언이 속한 블록의 동적 범위에 제한된다.[72]

외부 객체 문맥

중첩 클래스의 객체는 자신을 둘러싼 외부 객체 또는 블록의 문맥을 필요로 할 수 있다.

class Graph;
begin
    integer NextIdentifier;

    class Vertex;
    begin
        integer Identifier;

        NextIdentifier := NextIdentifier + 1;
        Identifier := NextIdentifier
    end;

    ref(Vertex) NewVertex;

    NewVertex :- new Vertex
end;

Vertex 객체는 외부 Graph 객체의 NextIdentifier를 사용한다. 따라서 내부 객체가 살아 있는 동안 언어 구현은 필요한 외부 문맥도 유효하게 유지해야 한다.

이 관계는 단순한 호출 스택 주소만 저장하는 방식으로는 항상 처리할 수 없다. 내부 객체가 생성 지점의 실행 이후에도 유지될 수 있으므로 구현은 정적 링크, 환경 객체 또는 이에 대응하는 구조로 외부 문맥을 보존할 수 있다.

참조와 별칭

여러 참조는 같은 객체를 가리킬 수 있다.

ref(Account) First, Second;

First :- new Account(100.0);
Second :- First;

Second.Deposit(50.0);

OutFix(First.Balance, 2, 10);
OutImage;

FirstSecond는 별개의 참조 변수지만 같은 객체를 나타낸다. 객체 하나를 여러 자료 구조와 다른 객체가 함께 참조할 수 있기 때문에, 객체 수명을 단순히 한 변수의 범위만으로 결정할 수 없다.

class Node;
begin
    ref(Node) Next, Previous
end;

ref(Node) A, B;

A :- new Node;
B :- new Node;

A.Next :- B;
B.Previous :- A;

객체 참조는 순환 구조도 만들 수 있다. 이러한 구조는 단순 참조 횟수만으로 회수하기 어려울 수 있으며, 도달 가능성을 추적하는 가비지 컬렉션과 잘 맞는다.

접근 불가능한 객체

객체를 가리키던 마지막 접근 경로가 사라지면 프로그램은 더 이상 해당 객체를 사용할 수 없다.

ref(Point) Current;

Current :- new Point(1.0, 2.0);
Current :- new Point(3.0, 4.0);

첫 번째 객체를 가리키는 다른 참조가 없다면 두 번째 대입 이후 첫 번째 객체에는 계산 가능한 참조 표현식으로 도달할 수 없다.

Current :- none;

이후 두 번째 객체에도 다른 참조가 없다면 해당 객체 역시 접근할 수 없게 된다. 언어 차원에서는 객체를 명시적으로 해제하는 문장이 필요하지 않다.

가비지 컬렉션

Simula Common Base Language는 구현이 가비지 컬렉션과 같은 기술을 이용해 객체의 실질적인 수명을 더 줄일 수 있다고 명시한다. 이는 프로그램에서 더 이상 접근할 수 없는 객체의 저장 공간을, 그 객체를 둘러싼 전체 블록이 끝나기 전에도 회수할 수 있다는 의미다.[73]

ref(Node) Temporary;

Temporary :- new Node;
Temporary :- none;

다른 참조가 없다면 생성된 Node는 회수 후보가 된다.

가비지 컬렉션의 구체적인 알고리즘은 언어 표준이 규정하지 않는다. 구현체는 다음과 같은 방법 가운데 하나 또는 여러 방법을 사용할 수 있다.

  • 표시 후 쓸기 방식
  • 복사 방식
  • 세대별 수집
  • 참조 계수와 추적 수집의 결합
  • 구현 환경의 기존 런타임 수집기 활용

사용자 프로그램에서 관찰할 수 있는 핵심 규칙은 여전히 접근 가능한 객체가 임의로 사라지지 않아야 한다는 것이다.

순환 참조

객체는 서로를 가리키는 순환 구조를 만들 수 있다.

class Node;
begin
    ref(Node) Next
end;

ref(Node) First, Second;

First :- new Node;
Second :- new Node;

First.Next :- Second;
Second.Next :- First;

외부 참조를 제거하면 두 객체는 서로를 계속 가리키지만 프로그램의 다른 부분에서는 도달할 수 없게 된다.

First :- none;
Second :- none;

추적 기반 가비지 컬렉터는 루트 집합에서 도달할 수 없는 두 객체를 함께 회수할 수 있다. 객체 그래프와 순환 참조를 자연스럽게 사용할 수 있다는 점은 Simula의 시뮬레이션 모델, 연결 목록과 객체 관계 표현에 중요하다.

배열과 텍스트 객체의 수명

배열과 텍스트 객체는 일반적인 단순 지역 변수와 다른 수명 특성을 가질 수 있다. Common Base Language는 배열과 텍스트 객체가 선언된 블록 인스턴스와 항상 동시에 삭제될 수 있는 것은 아니라고 설명한다.[74]

텍스트의 일부 프레임이 원래 텍스트 객체를 계속 참조할 수 있기 때문이다.

text Whole, Part;

Whole :- "SIMULA LANGUAGE";
Part :- Whole.Sub(1, 6);
Whole :- notext;

OutText(Part);
OutImage;

Whole 참조가 제거되어도 Part가 같은 문자 저장 영역의 일부를 계속 나타내므로 해당 저장 영역은 유지되어야 한다.

배열도 절차나 객체의 환경을 통해 계속 참조될 수 있다. 구현은 활성 참조와 언어의 정적·동적 범위 규칙을 고려해 저장 공간을 유지해야 한다.

지역 변수와 객체 필드

일반 블록의 지역 변수는 해당 블록 인스턴스에 속한다.

procedure Calculate;
begin
    integer Temporary;

    Temporary := 10
end;

TemporaryCalculate의 각 호출마다 독립적으로 존재하며 호출 인스턴스가 끝나면 더 이상 접근할 수 없다.

클래스에 선언된 변수는 객체 필드가 된다.

class Counter;
begin
    integer Value
end;

ref(Counter) A, B;

A :- new Counter;
B :- new Counter;

A.Value := 10;
B.Value := 20;

A.ValueB.Value는 서로 다른 객체에 속하므로 독립적으로 유지된다. 객체 필드의 수명은 단순한 클래스 본문 실행 시간보다 길 수 있으며 객체 자체의 수명을 따른다.

절차 활성 레코드와 재귀

재귀 절차는 호출마다 별도의 동적 인스턴스를 만든다.

integer procedure Factorial(N);
    value N;
    integer N;
begin
    if N <= 1 then
        Factorial := 1
    else
        Factorial := N * Factorial(N - 1)
end;

각 호출은 자신의 N과 결과 저장 위치를 가진다. 이러한 절차 활성 인스턴스는 일반적으로 후입선출 순서로 끝나므로 스택형 구현에 적합하지만, 표준은 실제 메모리 배치를 강제하지 않는다.

이름 호출 매개변수나 중첩 절차가 외부 문맥을 참조하는 경우에는 단순 값만 저장하는 것보다 추가적인 환경 정보가 필요할 수 있다.

준병렬 객체의 실행 문맥

detach된 객체는 지역 변수뿐 아니라 중단된 실행 위치를 보존한다.

class Worker;
begin
    integer Step;

    for Step := 1 step 1 until 3 do
    begin
        OutInt(Step, 3);
        OutImage;

        detach
    end
end;

객체가 detach된 뒤 다시 resume되면 이전 실행 위치와 Step 값을 사용해 계속 실행한다.

ref(Worker) W;

W :- new Worker;

resume(W);
resume(W);
resume(W);

따라서 준병렬 객체의 저장 공간에는 일반 필드 외에도 지역 실행 상태와 제어 문맥이 필요하다. 구현체는 이를 별도의 코루틴 스택, 활성 레코드 체인 또는 변환된 상태 기계로 나타낼 수 있다.

종료된 객체와 회수 가능한 객체

종료된 객체회수 가능한 객체는 같은 개념이 아니다.

class Data;
begin
    integer Value;

    Value := 42
end;

ref(Data) D;

D :- new Data;
OutInt(D.Value, 4);
OutImage;

Data 객체의 클래스 본문은 이미 끝났지만 D가 참조하므로 객체는 계속 사용 가능하다.

반대로 아직 클래스 본문이 논리적으로 끝나지 않았더라도 어떤 유효한 실행·참조 경로에서도 도달할 수 없는 객체라면 구현의 수명 규칙에 따라 회수 대상이 될 수 있다. 다만 준병렬 시스템의 detached 객체처럼 저장된 제어 구조가 객체를 계속 시스템 구성 요소로 유지하는 경우에는 단순한 사용자 참조 유무만으로 접근 불가능성을 판단할 수 없다.

명시적 소멸자의 부재

Simula는 C++의 소멸자나 수동 delete와 같은 일반적인 객체 파괴 문법을 제공하지 않는다.

ref(Resource) R;

R :- new Resource;
R :- none;

참조를 none으로 바꾸는 것은 객체를 즉시 파괴하라는 명령이 아니다. 해당 참조 변수와 객체의 연결만 제거한다. 다른 참조가 객체를 계속 가리키면 객체는 그대로 유효하다.

ref(Resource) First, Second;

First :- new Resource;
Second :- First;

First :- none;

! Second를 통해 객체에 계속 접근할 수 있다.;
Second.Use;

파일과 같은 외부 자원은 객체 메모리의 회수와 별도로 명시적인 Close 절차를 사용해야 한다.

ref(OutFile) Output;

Output :- new OutFile("result.txt");

if Output.Open(Blanks(80)) then
begin
    Output.OutText("Result");
    Output.OutImage;
    Output.Close
end;

가비지 컬렉션은 객체 저장 공간을 관리하지만 외부 파일 상태나 장치 자원을 자동으로 적절한 시점에 정리한다는 보장은 아니다.

값 대입과 참조 대입의 분리

Simula는 값 대입과 참조 대입을 문법적으로 분리한다.

integer Count;
ref(Point) Position;
text Buffer;

Count := 10;
Position :- new Point(1.0, 2.0);
Buffer :- Blanks(20);

:=는 값 타입 변수에 값을 저장하거나 텍스트 프레임에 문자값을 복사한다. :-는 객체 참조 또는 텍스트 참조를 대입한다. Common Base Language는 이를 각각 “becomes”와 “denotes”로 설명한다.[75]

이 구분은 객체 복사와 객체 공유를 혼동하지 않도록 한다.

ref(Point) A, B;

A :- new Point(1.0, 2.0);
B :- A;

B :- AA의 좌표를 복사한 새 Point를 만드는 것이 아니라 같은 객체를 나타낸다. 독립된 객체가 필요하면 new를 다시 사용해야 한다.

B :- new Point(A.X, A.Y);

초기값

동적으로 생성된 변수와 객체 속성의 초기 상태는 타입과 표준 환경의 초기화 규칙에 영향을 받는다. 그러나 프로그램의 의미를 명확히 유지하려면 클래스 본문이나 절차 진입 직후 필요한 값을 명시적으로 지정하는 편이 적합하다.

class Account(InitialBalance);
    value InitialBalance;
    real InitialBalance;
begin
    real Balance;
    Boolean Frozen;

    Balance := InitialBalance;
    Frozen := false
end;

특히 참조 변수는 사용 전에 객체 또는 none으로 명확하게 설정하고, 배열 요소 역시 읽기 전에 값을 채우는 방식이 예측 가능한 코드를 만든다.

타입 검사와 런타임 오류

Simula에서 다음과 같은 상황은 타입 또는 참조 관련 오류가 될 수 있다.

  • 서로 관련 없는 클래스 참조 사이의 대입
  • 실제 객체와 맞지 않는 하향 참조 대입
  • 호환되지 않는 클래스에 대한 qua
  • none을 통한 원격 속성 접근
  • notext의 유효 문자를 읽거나 쓰려는 동작
  • 잘못된 배열 인덱스
  • 결과 타입과 맞지 않는 절차 결과 대입
ref(Vehicle) V;
ref(Car) C;

V :- new Vehicle;

if V in Car then
    C :- V qua Car
else
begin
    OutText("The object is not a Car");
    OutImage
end;

실제 타입을 먼저 검사하면 잘못된 하향 한정으로 인한 런타임 오류를 피할 수 있다.

if V =/= none then
    V.Move(10.0);

Simula에는 현대 언어의 선택 타입이나 null 안전 연산자가 없으므로, none 가능성이 있는 참조는 명시적으로 검사한다.

메모리 모델의 특징

Simula의 메모리 및 수명 모델은 다음 구조를 결합한다.

  • 일반 블록과 절차 호출은 동적 블록 인스턴스를 생성한다.
  • 클래스 객체는 new로 동적으로 생성된다.
  • 객체는 종료된 뒤에도 참조가 있으면 유지될 수 있다.
  • 여러 참조는 같은 객체를 공유할 수 있다.
  • 객체와 텍스트는 순환하거나 부분 프레임을 공유하는 구조를 만들 수 있다.
  • 중첩 객체는 필요한 외부 정적 문맥을 유지한다.
  • detached 객체는 데이터뿐 아니라 실행 위치도 보존한다.
  • 접근 불가능한 객체는 구현체의 가비지 컬렉션으로 회수될 수 있다.
  • 객체의 최대 동적 범위는 클래스 선언을 포함한 블록 인스턴스의 범위에 제한된다.
  • 구체적인 스택·힙 구조와 수집 알고리즘은 구현에 맡겨진다.

Simula는 사용자가 주소 계산과 수동 해제를 직접 수행하는 저수준 메모리 모델보다, 클래스와 블록의 동적 인스턴스 및 객체 참조 관계를 중심으로 저장 공간을 추상화한다. 이러한 구조 덕분에 시뮬레이션 대상과 복잡한 객체 그래프를 동적으로 생성하고 공유할 수 있으며, 객체의 저장 공간은 프로그램에서의 도달 가능성과 언어 실행 문맥에 따라 관리된다.

타입 체계와 메모리 관리는 서로 분리되어 있지 않다. ref(C)의 클래스 한정은 어떤 객체를 가리킬 수 있는지와 어떤 필드를 참조로 추적해야 하는지를 정하고, 접두 클래스 계층은 객체의 전체 구조와 가상 절차 바인딩을 결정한다. 텍스트와 배열의 타입 정보는 공유되는 저장 영역과 요소 구조를 해석하는 데 필요하며, 준병렬 객체의 클래스 구조는 데이터와 보존된 실행 상태를 함께 관리하게 한다.

이러한 설계는 이후 가비지 컬렉션을 사용하는 클래스 기반 언어의 일반적인 구조와 이어진다. 다만 Simula는 단순한 힙 객체만이 아니라 ALGOL 블록, 중첩 클래스, 코루틴과 객체 수명을 하나의 언어 의미론 안에서 결합했다는 점에서 고유한 메모리 모델을 가진다.

구현과 실행 환경

Simula는 특정 컴퓨터나 운영체제 하나에 종속된 언어로 머물지 않고, 여러 세대의 컴퓨터 시스템에 맞춘 컴파일러와 실행 환경으로 구현되었다. 최초의 Simula I 구현은 UNIVAC 1107용 ALGOL 60 컴파일러를 확장하는 방식으로 만들어졌고, Simula 67은 이후 UNIVAC 1100 계열, CDC 3000·6000 계열, IBM System/360·370과 DECsystem-10 등으로 확산되었다.

초기 구현은 각 시스템의 기계 구조와 운영체제에 맞추어 별도로 개발되는 경우가 많았다. 이후에는 중간 언어, C 코드 생성과 가상 머신을 이용해 하나의 구현을 여러 플랫폼으로 옮기려는 방식이 발전했다. 현대에 사용할 수 있는 구현도 Simula 소스를 C 또는 Java 바이트코드와 같은 이식 가능한 대상으로 변환하거나 자체 가상 머신에서 실행하는 구조를 사용한다.

Simula의 공식 언어 정의는 1968년의 Common Base Language, 1970년 개정판과 이후 Simula Standards Group의 변경 사항을 거쳐 1986년 표준으로 정리되었다. 1986년 8월 25일자로 정의된 Simula Standard가 현재까지 마지막으로 널리 인정되는 표준이며, 그 뒤에 새로운 언어 세대를 정의하는 Simula 90이나 Simula 2000과 같은 후속 표준은 제정되지 않았다.[76]

따라서 언어 설계와 표준화의 적극적인 개발은 1986년 표준에서 사실상 마무리되었다고 볼 수 있다. 그러나 이는 모든 컴파일러와 실행 환경의 개발이 같은 해에 끝났다는 뜻은 아니다. 기존 구현은 이후에도 유지·이식되었고, GNU Cim과 Portable Simula Revisited 같은 구현이 새 운영체제와 런타임에서 Simula 프로그램을 실행할 수 있도록 개발되었다.

최초의 Simula I 컴파일러

Simula I의 첫 구현은 노르웨이 컴퓨팅 센터가 도입한 UNIVAC 1107을 대상으로 개발되었다. 달과 개발진은 기존 UNIVAC ALGOL 60 컴파일러를 기반으로 프로세스, 활동, 시뮬레이션 시간과 사건 제어에 필요한 기능을 추가했다.

컴파일러의 첫 동작 가능한 판은 1964년 12월에 완성되었고, 1965년 1월부터 실제 사용이 가능해졌다. 이 구현은 Simula I 소스를 UNIVAC 1107의 기계 코드로 변환하고, 프로세스 실행과 시뮬레이션을 지원하는 런타임 환경을 함께 제공했다.[77]

초기 컴파일러는 언어 설계와 동시에 만들어졌다. 문법이나 실행 모델을 먼저 완전히 확정한 뒤 구현한 것이 아니라, 실제 컴파일러를 개발하고 시뮬레이션 프로그램을 실행하면서 언어의 문제점을 발견하고 수정했다.

Simula I은 이후 Burroughs B5500과 소련의 Ural-16 등에도 구현되었다. 그러나 Simula 67이 범용 언어로 정리된 뒤에는 새로운 구현의 중심이 Simula I에서 Simula 67로 이동했다.

초기 Simula 67 구현

Simula 67의 언어 정의는 여러 조직이 서로 호환되는 구현을 개발할 수 있도록 Common Base로 정리되었다. 1960년대 말부터 1970년대 초까지 대표적인 구현은 다음과 같다.

구현 대상개발 주체주요 특징
UNIVAC 1100 계열노르웨이 컴퓨팅 센터초기 Simula 경험을 이어받은 구현
IBM System/360·370노르웨이 컴퓨팅 센터IBM 대형 컴퓨터 환경 지원
CDC 3000 계열오슬로 대학교와 Kjeller의 공동 컴퓨팅 조직Simula 67 Common Base 기반 구현
CDC 6000 계열Control Data 계열 개발진대형 과학 계산 시스템 지원
DECsystem-10스웨덴 국방연구소대학과 연구기관에서 널리 사용된 구현

이들 컴파일러는 동일한 언어 정의를 따르면서도 코드 생성, 메모리 관리, 입출력과 운영체제 연동 방식에서는 차이를 가졌다. Simula Standards Group은 구현 간 차이를 줄이고 소스 코드의 이식성을 높이기 위해 언어 정의를 관리했다.[78]

초기 Simula 구현은 주로 컴파일 방식으로 실행되었다. 소스 코드는 기계 코드 또는 대상 시스템의 저수준 코드로 번역되었으며, 객체 생성, 가비지 컬렉션, 코루틴과 시뮬레이션 사건 목록을 처리하는 런타임 시스템이 프로그램과 함께 사용되었다.

컴파일과 실행 구조

Simula 구현은 일반적으로 다음 요소로 구성된다.

  1. Simula 소스 코드를 읽는 어휘·구문 분석기
  2. 선언, 타입과 클래스 계층을 검사하는 의미 분석기
  3. 접두 클래스와 가상 절차를 처리하는 클래스 처리기
  4. 대상 코드 또는 중간 코드를 생성하는 컴파일러 백엔드
  5. 객체, 텍스트, 파일과 가비지 컬렉션을 지원하는 런타임
  6. SIMSETSIMULATION 시스템 클래스
  7. 표준 입출력·수학·난수 라이브러리

개념적인 실행 흐름은 다음과 같다.

Simula 소스 코드
→ 어휘 및 구문 분석
→ 타입과 클래스 계층 검사
→ 중간 표현 생성
→ C 코드, 기계 코드 또는 Java 바이트코드 생성
→ 플랫폼 컴파일러 또는 가상 머신
→ Simula 런타임과 함께 실행

클래스와 가상 절차는 실행 전에 가능한 범위에서 정적으로 분석된다. 객체의 실제 클래스에 따라 달라지는 가상 절차 호출, qua와 하향 참조 대입은 런타임의 클래스 정보를 사용한다.

코루틴과 Process 객체를 지원하는 구현은 각 객체의 중단 위치와 실행 문맥을 저장해야 한다. 초기 구현은 별도의 스택과 제어 블록을 사용했고, 현대 구현은 Java 스레드·가상 스레드 또는 변환된 상태 구조를 사용할 수 있다.

별도 컴파일

Simula Standard는 프로그램을 여러 모듈로 나누고 외부 클래스와 절차를 별도로 컴파일하는 구조를 정의한다. 대규모 프로그램과 재사용 가능한 클래스 라이브러리를 하나의 소스 파일에 모두 작성할 필요가 없다.

다음은 별도 클래스 모듈을 사용하는 개념적인 예다.

external class Customer;

begin
    ref(Customer) C;

    C :- new Customer("Example");
    C.Print
end;

외부 클래스의 선언 정보에는 클래스 매개변수, 접두 클래스, 가상 절차와 외부에서 접근할 수 있는 속성이 포함되어야 한다. 컴파일러는 실제 구현을 연결하기 전에도 이 정보를 이용해 타입 검사를 수행할 수 있다.

GNU Cim은 완전한 타입 검사를 유지하는 별도 컴파일을 주요 기능으로 제공한다. Portable Simula도 별도 컴파일 모듈의 속성 정보를 생성하고 다른 컴파일 단위에서 이용하는 구조를 지원한다.[79]

런타임 시스템

Simula 프로그램은 컴파일된 사용자 코드만으로 실행되지 않는다. 다음과 같은 언어 기능을 지원하는 런타임 시스템이 필요하다.

  • 동적 객체 생성
  • 클래스와 접두 클래스 정보
  • 가상 절차 바인딩
  • 객체 참조 검사
  • 텍스트 프레임과 부분 텍스트
  • 배열 범위 정보
  • 가비지 컬렉션
  • 파일 및 이미지 입출력
  • 코루틴의 중단과 재개
  • 사건 목록과 시뮬레이션 시간
  • 난수와 확률 분포 함수

간단한 프로그램도 출력 기능을 위해 표준 환경을 사용한다.

begin
    OutText("Runtime: ");
    OutText(SimulaId);
    OutImage
end;

SimulaId는 실행 중인 Simula 시스템과 구현 정보를 나타내는 텍스트를 반환한다. 실제 반환 형식은 구현체가 정한다.

begin
    OutText("Current date and time: ");
    OutText(DateTime);
    OutImage;

    OutText("CPU time: ");
    OutFix(CPUTime, 3, 12);
    OutImage
end;

날짜, 실제 시간과 CPU 사용 시간도 표준 환경 절차를 통해 확인할 수 있다.

가비지 컬렉션 구현

Simula는 동적으로 생성된 객체를 명시적인 delete로 제거하지 않는다. 구현체의 런타임은 더 이상 접근할 수 없는 객체를 탐지하고 저장 공간을 회수한다.

begin
    ref(Node) First, Second;

    First :- new Node;
    Second :- new Node;

    First.Next :- Second;
    Second.Next :- First;

    First :- none;
    Second :- none
end;

두 객체가 서로를 참조하더라도 외부의 실행 가능한 참조에서 도달할 수 없다면 추적형 가비지 컬렉터가 함께 회수할 수 있다.

초기 컴퓨터는 메모리가 제한적이었으므로 가비지 컬렉션의 성능과 객체 표현 방식이 구현에서 중요한 문제였다. 컴파일러마다 객체 헤더, 클래스 정보와 참조 추적 방식이 달랐지만, 프로그램에서 관찰되는 수명 의미는 표준을 따라야 했다.

S-Port

1979년 노르웨이 컴퓨팅 센터와 에든버러의 Program Library Unit은 S-Port 프로젝트를 시작했다. 목표는 특정 기계마다 전체 Simula 컴파일러를 다시 작성하지 않고, 공통 프런트엔드와 이식 가능한 중간 표현을 이용해 여러 시스템에 옮길 수 있는 구현을 만드는 것이었다.

S-Port는 Simula에 가까운 시스템 구현 언어인 Simuletta, 중간 코드인 S-Code와 플랫폼별 환경 인터페이스를 사용했다.

Simula 소스
→ S-Port 프런트엔드
→ S-Code
→ 플랫폼별 백엔드
→ 기계 코드와 런타임

이 구조에서는 언어 분석과 대부분의 컴파일 처리를 공통으로 유지하고, 새로운 컴퓨터에 필요한 백엔드와 런타임 부분만 개발할 수 있었다.

S-Port 프로젝트는 1980년대 이후 Simula a.s.에 의해 이어졌다. 당시의 마지막 알려진 소스와 설계 문서는 보존되었으며, 현대의 Portable Simula 프로젝트는 이 자료를 이용해 S-Port 시스템을 복원했다.[80]

Simula a.s.와 상용 구현

Simula의 보급과 지원을 위해 상용 또는 기관 중심의 구현과 배포도 이루어졌다. Simula a.s.는 S-Port 계열 구현과 Simula 개발 환경을 여러 플랫폼에 제공했다. 대학과 연구기관에서는 DEC, IBM, UNIVAC와 Unix 계열 시스템을 대상으로 한 구현을 사용했다.

이들 구현은 당시의 대형 컴퓨터, 워크스테이션과 운영체제에 맞추어 유지되었으나, 해당 플랫폼이 퇴역하고 사용자 기반이 감소하면서 대부분 일반적인 현대 시스템에서 직접 사용하기 어려운 레거시 소프트웨어가 되었다.

Simula a.s.의 소스와 문서 가운데 일부는 이후 보존 프로젝트와 Portable Simula Revisited의 자료로 사용되었다. 현재 Simula Research Laboratory라는 노르웨이 연구기관도 존재하지만, 이는 Simula 언어의 업적을 기념해 이름을 붙인 별도의 현대 연구기관이며 Simula 언어의 공식 컴파일러 개발 조직과 동일하지 않다.

Lund Simula

Lund Simula는 스웨덴 룬드 대학교 계열에서 개발된 Simula 구현이다. Unix 환경을 포함한 여러 시스템에서 Simula 프로그램을 실행하는 데 사용되었다.

Lund Simula와 기존 대학·상용 구현은 Simula가 교육과 연구에서 실제 사용되던 시기의 중요한 구현이지만, 오늘날 일반 개발자가 공식 패키지 저장소에서 쉽게 설치해 사용하는 형태로 활발히 배포되지는 않는다. 오래된 소스 코드와 바이너리는 보존되어 있더라도 현대 운영체제와 컴파일러에 맞추려면 별도의 수정이 필요할 수 있다.

GNU Cim

GNU Cim은 Simula 소스를 C 코드로 변환하는 자유 소프트웨어 컴파일러다. 컴파일러와 런타임 시스템 자체도 C로 작성되었으며, 생성된 C 코드는 시스템의 C 컴파일러를 거쳐 기계 코드로 변환된다.[81]

Simula 소스
→ GNU Cim
→ C 소스
→ 시스템 C 컴파일러
→ 실행 파일

GNU Cim은 다음 기능을 제공한다.

  • 클래스와 접두 클래스
  • 가상 절차
  • 객체 참조와 가비지 컬렉션
  • 별도 컴파일과 타입 검사
  • 코루틴
  • 프로세스 기반 시뮬레이션
  • 외부 C 절차 호출
  • Unix 계열 환경에서의 컴파일

일반적인 명령 형태는 다음과 같다.

cim program.sim

성공하면 C 코드 생성과 시스템 컴파일·연결 과정을 거쳐 실행 파일이 만들어진다.

begin
    OutText("Hello from GNU Cim");
    OutImage
end;

GNU Cim은 Simula를 현대 Unix 계열 시스템에서 실행할 수 있게 한 중요한 구현이지만, 현재의 주류 GNU 컴파일러 제품군처럼 활발한 릴리스와 대규모 유지보수가 계속되는 프로젝트는 아니다. 공개 소스는 확보할 수 있으나, 빌드 스크립트와 C 코드가 오래된 개발 환경을 전제로 하므로 최신 Linux 배포판과 C 컴파일러에서 추가 수정이 필요할 수 있다.

GNU Cim 문서는 버전 4를 실험판으로, 버전 3을 안정판으로 설명한다. 이 설명과 빌드 체계는 프로젝트가 오래된 상태로 보존되고 있음을 보여준다. 따라서 GNU Cim은 새 프로젝트의 일반적인 생산 도구보다 기존 Simula 코드 실행, 언어 연구와 역사적 실험에 적합한 구현으로 볼 수 있다.

Portable Simula Revisited

Portable Simula Revisited는 Simula Standard를 현대 Java 환경에서 구현하는 오픈소스 프로젝트다. Simula 소스를 Java 기반 실행 형식으로 변환하고, Simula의 객체 모델과 표준 환경을 Java 런타임 위에 구현한다.

현재 배포판은 Windows, Linux와 macOS처럼 Java를 실행할 수 있는 운영체제에서 사용할 수 있다. 2026년 7월 현재 프로젝트의 최신 기본 릴리스는 Java 25 이상을 요구하며, Java 17 및 Java 21 환경을 위한 호환 설치판도 별도로 제공된다.[82]

설치 프로그램은 JAR 파일로 배포되며 터미널에서 다음과 같이 실행할 수 있다.

java -jar SimulaSetup.jar

설치가 완료되면 제공되는 컴파일러와 개발 환경을 통해 Simula 소스를 컴파일하고 실행할 수 있다.

begin
    class Greeting(Message);
        text Message;
    begin
        procedure Print;
        begin
            OutText(Message);
            OutImage
        end
    end;

    ref(Greeting) G;

    G :- new Greeting("Portable Simula");
    G.Print
end;

Portable Simula는 Java 가상 머신의 이식성과 메모리 관리 기능을 활용한다. 객체와 텍스트 런타임은 Java 객체로 표현될 수 있으며, Simula의 도달 불가능 객체는 Java 가비지 컬렉터가 회수할 수 있다.

Java 코드 및 바이트코드 생성

Portable Simula의 구현은 Simula 프로그램을 Java 실행 환경에 맞추는 여러 단계를 사용한다.

Simula 소스
→ Simula 구문 및 타입 분석
→ Java 표현 또는 클래스 파일 생성
→ 바이트코드 보정
→ Java Virtual Machine
→ Simula 런타임 클래스

Simula의 goto, 코루틴과 준병렬 실행은 일반적인 Java 소스 구조에 그대로 대응하지 않는다. 구현은 생성된 코드 또는 클래스 파일을 변환해 Simula의 제어 흐름을 재현한다.

최근 Portable Simula 구현은 Java의 가상 스레드도 활용한다. 프로젝트는 가상 스레드가 많은 수의 시뮬레이션 프로세스를 실행할 때 실행 시간과 자원 사용을 개선할 수 있다고 설명하며, 최대 백만 개 수준의 프로세스를 사용하는 예제도 제공한다.[83]

이는 원래의 Simula 코루틴을 현대 Java의 실제 병렬 스레드로 단순 교체한다는 뜻은 아니다. 언어가 규정하는 협력적 실행과 사건 순서를 보존하면서, 구현 내부의 실행 문맥을 가볍게 유지하는 수단으로 Java 가상 스레드를 활용할 수 있다는 의미다.

복원된 S-Port Simula

Portable Simula 프로젝트는 기존 S-Port의 보존 자료를 이용해 S-Port 시스템도 복원했다. 새로운 백엔드 컴파일러인 BEC가 S-Code를 Simula Virtual Machine용 코드로 컴파일하고, 자체 SVM 구현이 이를 실행한다.

Simula 소스
→ 복원된 S-Port 프런트엔드
→ S-Code
→ BEC
→ Simula Virtual Machine
→ 실행

복원된 S-Port Simula도 Java 25 이상 환경을 위한 현재 설치판이 제공된다. 이는 1980년대 이식형 컴파일러 구조를 단순히 소스 보관 상태로 남겨 둔 것이 아니라, 현대 컴퓨터에서 다시 실행할 수 있도록 재구성한 것이다.[84]

현재 설치와 실행

2026년 현재 Simula를 실제로 실행하는 가장 접근하기 쉬운 방법은 Portable Simula Revisited를 사용하는 것이다.

기본 설치 과정은 다음과 같다.

  1. Java 25 이상을 설치한다.
  2. Java 실행 파일이 환경 변수와 경로에 등록되어 있는지 확인한다.
  3. Portable Simula 설치 JAR을 내려받는다.
  4. java -jar SimulaSetup.jar를 실행한다.
  5. 설치된 Simula 컴파일러에서 .sim 소스를 컴파일한다.
  6. Java 런타임을 통해 생성된 프로그램을 실행한다.

Java 17이나 Java 21 환경을 유지해야 하는 경우 해당 런타임용 설치판을 선택할 수 있다.

현재 실행 가능 여부를 확인하는 최소 프로그램은 다음과 같다.

begin
    OutText("Simula is running");
    OutImage;

    OutText("Implementation: ");
    OutText(SimulaId);
    OutImage
end;

따라서 Simula는 에뮬레이터로만 볼 수 있는 사어가 아니다. 현대 운영체제에서 소스를 컴파일하고 프로그램을 실행할 수 있는 구현이 실제로 존재한다.

표준 개발의 종료 시점

Simula 언어의 개발이 정확히 한 날짜에 완전히 종료되었다고 보기는 어렵다. 서로 다른 층위의 개발이 별도로 진행되었기 때문이다.

구분주요 시점
Simula I 설계1961년~1965년
Simula 67 핵심 설계1965년~1967년
Common Base 확정1967년~1968년
Common Base 개정1970년
접근 제어와 환경 기능을 포함한 후기 표준화1970년대~1980년대
마지막 주요 언어 표준1986년 8월 25일
개별 컴파일러와 이식 작업1986년 이후에도 계속
Portable Simula의 현대화2010년대 이후 현재까지

Simula 67의 핵심 언어 설계는 1967년에 완성되었지만, 그 시점 이후에도 표준 환경, 파일 입출력, 접근 제어, 별도 컴파일과 구현 호환성에 관한 작업이 이어졌다.

1986년 표준은 이 장기간의 언어 정의를 통합한 마지막 주요 표준이다. 이후에는 새로운 기능을 지속적으로 추가하는 언어 표준 개발보다 기존 표준의 보존, 오류 정리와 현대 플랫폼용 구현에 활동의 중심이 옮겨갔다.

따라서 다음과 같이 구분하는 것이 적절하다.

  • 핵심 언어 설계 완성: 1967년
  • 마지막 주요 표준 완성: 1986년
  • 컴파일러 개발과 이식: 이후에도 일부 계속
  • 현대 보존 구현 개발: 2026년 현재도 진행

현재 사용 상태

Simula는 현재 C++, Java, Python이나 C#처럼 산업과 교육 전반에서 널리 사용되는 언어가 아니다. 새로운 상용 소프트웨어, 웹 서비스, 모바일 애플리케이션이나 운영체제를 Simula로 개발하는 사례는 거의 찾아보기 어렵다.

이산 사건 시뮬레이션 분야에서도 Simula가 개척한 프로세스 상호작용 모델은 여러 후속 언어와 라이브러리에 계승되었지만, 실제 프로젝트에서는 현대적인 시뮬레이션 프레임워크와 범용 언어가 주로 사용된다. 객체 지향 개발에서도 Simula의 개념은 널리 살아남았으나 원래 언어 자체의 사용자는 크게 감소했다.

현재 Simula의 주요 사용 목적은 다음과 같다.

  • 프로그래밍 언어 역사 연구
  • 객체 지향 프로그래밍의 기원 교육
  • 기존 Simula 프로그램의 보존과 실행
  • 이산 사건 시뮬레이션 모델 연구
  • 컴파일러와 런타임 구현 실험
  • 오래된 과학·산업 소프트웨어의 복원
  • 코루틴과 프로세스 상호작용 모델의 연구

활발한 패키지 생태계, 현대적인 범용 IDE 지원, 상업용 클라우드 배포 환경과 대규모 신규 개발자 공동체는 형성되어 있지 않다. 따라서 일반적인 소프트웨어 개발 언어로서는 사실상 퇴역한 상태에 가깝다.

그러나 퇴역과 소멸은 구분해야 한다. Simula의 표준은 남아 있고, 언어 문서와 교육 자료가 보존되어 있으며, 최신 Java 환경에서 실행 가능한 컴파일러가 배포된다. 소스 코드를 작성하고 컴파일해 실행하는 행위 자체는 현재도 가능하다.

퇴출된 언어인지의 구분

Simula의 현재 상태는 다음과 같이 설명할 수 있다.

판단상태
새로운 산업 프로젝트의 주류 언어아님
일반 대학의 기본 프로그래밍 교육 언어거의 아님
언어 표준의 지속적인 기능 확장1986년 이후 사실상 중단
기존 코드와 문서의 보존계속됨
현대 운영체제에서 실행 가능가능
현재 다운로드 가능한 구현존재
구현체의 현대화 작업Portable Simula에서 계속됨
역사적 영향객체 지향 언어 전반에 지속

따라서 Simula를 “완전히 퇴출되어 사용할 수 없는 언어”라고 표현하면 현재 상태를 정확하게 설명하지 못한다. 실무 생태계에서는 퇴역했지만, 보존·교육·연구용으로 실행 가능한 언어라고 설명하는 편이 적절하다.

구현별 현재 위치

구현현재 위치
초기 UNIVAC·CDC·IBM 구현역사적 시스템 또는 에뮬레이션 대상
DECsystem-10 Simula역사적 구현과 기존 코드 보존
Simula a.s. 계열상용 지원은 사실상 종료되고 자료 중심으로 보존
Lund Simula레거시 대학 구현
GNU Cim공개 소스가 남아 있는 C 기반 레거시 컴파일러
Portable Simula Revisited현대 Java 환경에서 현재 사용 가능한 구현
복원된 S-Port SimulaS-Port 구조를 현대 환경에서 실행하기 위한 보존 구현

Portable Simula Revisited가 존재한다고 해서 Simula가 다시 대규모 현역 언어가 된 것은 아니다. 이 프로젝트의 의미는 언어를 현대 컴퓨터에서 실제로 실행 가능한 상태로 보존하고, Simula의 독특한 객체·코루틴·시뮬레이션 모델을 연구할 수 있게 한다는 데 있다.

현대 환경에서의 제약

현재 Simula를 사용할 때는 다음과 같은 환경 차이를 고려해야 한다.

  • 오래된 Simula 소스는 특정 컴파일러 확장에 의존할 수 있다.
  • 문자 집합과 파일 입출력 규칙이 현대 Unicode 환경과 다를 수 있다.
  • 운영체제별 파일 이름과 경로 처리에 차이가 있다.
  • 초기 구현 전용 외부 절차와 시스템 호출은 그대로 사용할 수 없다.
  • 부동소수점 정밀도와 난수열은 원래 시스템과 달라질 수 있다.
  • 프로세스 스케줄링의 내부 구현은 컴파일러마다 다를 수 있다.
  • GNU Cim은 최신 C 도구 체계에서 직접 빌드되지 않을 수 있다.
  • Portable Simula는 Java 런타임 버전에 의존한다.

표준 문법과 시스템 클래스만 사용한 프로그램은 비교적 이식하기 쉽지만, 특정 컴퓨터의 파일 시스템, 그래픽, 터미널이나 외부 언어 인터페이스에 의존한 프로그램은 현대 구현에 맞게 수정해야 한다.

구현과 언어의 분리

Simula의 현재 상태를 이해하려면 언어와 구현을 분리해야 한다.

언어는 1986년 표준으로 고정된 문법과 의미 체계다. 클래스, 객체, 참조, 접두 클래스, 가상 절차, 코루틴, 텍스트와 시뮬레이션 시스템의 동작을 규정한다.

구현은 이 언어 정의를 특정 환경에서 실행하는 컴파일러와 런타임이다. GNU Cim은 C를 중간 대상으로 사용하고, Portable Simula는 Java 환경을 사용하며, 복원된 S-Port는 S-Code와 Simula Virtual Machine을 이용한다.

Simula Standard
├── 초기 기계별 네이티브 컴파일러
├── S-Port 이식형 컴파일러
├── GNU Cim의 C 코드 생성
└── Portable Simula의 Java 실행 환경

동일한 Simula 프로그램이라도 구현 내부의 객체 표현, 스케줄러, 가비지 컬렉터와 생성 코드 구조는 달라질 수 있다. 구현이 표준이 규정한 프로그램 동작을 보존한다면 내부 기술은 자유롭게 선택할 수 있다.

보존 언어로서의 의미

Simula는 실행 가능한 역사 자료라는 점에서 중요하다. 단순히 논문과 문법서만 남은 언어가 아니라, 클래스와 객체, 가상 절차, 코루틴과 프로세스 기반 시뮬레이션을 실제 프로그램으로 작성하고 실행할 수 있다.

Simulation
begin
    Process class HistoricalProcess(Name);
        text Name;
    begin
        OutText(Name);
        OutText(" begins at ");
        OutFix(Time, 2, 8);
        OutImage;

        Hold(5.0);

        OutText(Name);
        OutText(" ends at ");
        OutFix(Time, 2, 8);
        OutImage
    end;

    Activate new HistoricalProcess("Simula process");
    Hold(10.0)
end;

이 코드를 현대 구현에서 실행하면 1960년대에 형성된 프로세스 상호작용 모델이 어떤 방식으로 작동했는지 직접 확인할 수 있다.

Simula의 산업적 생명 주기는 사실상 끝났지만, 언어의 개념은 현대 객체 지향 언어, 시뮬레이션 프레임워크와 코루틴 시스템 속에 남아 있다. 현재 구현의 목적도 새로운 대규모 생태계를 만드는 것보다, 이러한 원형을 정확하게 보존하고 실행 가능하게 유지하는 데 가깝다.

활용 분야

Simula는 처음에는 복잡한 시스템의 동작을 표현하는 이산 사건 시뮬레이션 언어로 개발되었지만, Simula 67에서는 특정 분야에 한정되지 않는 범용 프로그래밍 언어로 확장되었다. 시뮬레이션 기능도 핵심 문법에 모두 고정하지 않고 SIMSETSIMULATION 시스템 클래스로 분리했기 때문에, 일반적인 수치 계산, 자료 처리, 그래픽스와 소프트웨어 구조화에도 사용할 수 있었다.[85]

Common Base Language는 Simula 67을 특정 응용 분야에 맞추기 위해 그 분야의 개념과 연산을 제공하는 접두 클래스를 정의할 수 있다고 설명한다. SIMULATION이 이산 사건 시뮬레이션을 위한 응용 계층인 것처럼, 그래픽스·자료 처리·통신이나 다른 문제 영역을 위한 클래스 계층을 작성해 언어 환경을 확장할 수 있었다.[86]

Simula는 역사적으로 시뮬레이션, 공정 모델링, 통신 프로토콜, 알고리즘 연구, 컴퓨터 그래픽스, 과학 자료 처리, 조판, 전자 회로 설계와 프로그래밍 교육 등에 사용되었다. 그러나 2026년 현재 이러한 분야의 신규 프로젝트에서 Simula 자체가 일반적으로 선택되지는 않는다. 언어가 개척한 객체 지향 모델과 프로세스 기반 시뮬레이션 구조는 후속 언어와 현대 시뮬레이션 프레임워크에 계승되었으며, Simula의 직접적인 사용은 주로 기존 프로그램 보존, 교육과 프로그래밍 언어 연구에 남아 있다.[87]

운용 연구

Simula의 초기 목적은 운용 연구에서 다루는 복잡한 시스템을 프로그램으로 표현하는 것이었다. 운용 연구에서는 한정된 자원, 작업 순서, 대기 행렬, 이동 경로와 확률적인 사건이 전체 시스템의 성능에 어떤 영향을 주는지 분석한다.

예를 들어 창구 수를 늘렸을 때 평균 대기 시간이 얼마나 줄어드는지, 공장에 새로운 장비를 추가하면 병목이 어디로 이동하는지, 차량 배차 간격을 변경하면 승객의 대기 시간이 어떻게 달라지는지를 실제 시스템에 적용하기 전에 모델로 시험할 수 있다.

Simula의 클래스와 객체는 모델의 구성 요소를 표현하고, Process 객체는 시간에 따른 행동을 나타내며, 사건 목록은 전체 실행 순서를 관리한다.

Simulation
begin
    Process class Job(Number);
        value Number;
        integer Number;
    begin
        OutText("Job ");
        OutInt(Number, 3);
        OutText(" begins at ");
        OutFix(Time, 2, 8);
        OutImage;

        Hold(4.0);

        OutText("Job ");
        OutInt(Number, 3);
        OutText(" ends at ");
        OutFix(Time, 2, 8);
        OutImage
    end;

    Activate new Job(1);
    Activate new Job(2) delay 2.0;

    Hold(10.0)
end;

각 작업은 독립된 객체로 존재하며 자신이 시작하고 종료되는 시간 순서를 보유한다. 모델 작성자는 여러 실행에서 작업 수, 처리 시간과 자원 수를 변경해 결과를 비교할 수 있다.

대기 행렬과 서비스 시스템

은행 창구, 병원 접수, 콜센터, 매표소와 컴퓨터 작업 큐처럼 고객이나 작업이 제한된 서비스를 기다리는 시스템은 Simula의 대표적인 활용 대상이었다.

대기 행렬 모델은 일반적으로 다음 요소로 구성된다.

  • 고객이나 작업의 도착 분포
  • 서비스를 제공하는 자원 수
  • 서비스에 걸리는 시간
  • 대기열의 순서 규칙
  • 대기 가능한 최대 인원
  • 고객의 이탈 또는 우선순위
  • 평균 대기 시간과 자원 사용률
Simulation
begin
    ref(Head) Queue;
    Boolean ServerBusy;

    Process class Customer(Number);
        value Number;
        integer Number;
    begin
        long real ArrivalTime;

        ArrivalTime := Time;

        if ServerBusy then
            Wait(Queue);

        ServerBusy := true;

        OutText("Customer ");
        OutInt(Number, 3);
        OutText(" waited ");
        OutFix(Time - ArrivalTime, 2, 8);
        OutImage;

        Hold(3.0);

        if Queue.Empty then
            ServerBusy := false
        else
        begin
            ref(Process) NextCustomer;

            NextCustomer :-
                Queue.First qua Process;

            NextCustomer.Out;
            Activate NextCustomer
        end
    end;

    Queue :- new Head;

    Activate new Customer(1);
    Activate new Customer(2) delay 1.0;
    Activate new Customer(3) delay 2.0;

    Hold(20.0)
end;

Wait는 고객을 대기열로 옮기고, 서비스가 끝난 고객은 대기열 첫 항목을 다시 활성화한다. 같은 모델에서 창구 수, 도착 간격과 서비스 시간을 바꾸면 여러 운영 정책을 비교할 수 있다.

제조와 생산 공정

공장과 생산 설비는 Simula의 프로세스 상호작용 모델에 잘 맞는 분야다. 제품, 작업, 기계, 운반 장치와 작업자를 객체로 정의하고, 공정 단계 사이의 이동과 대기를 프로세스로 표현할 수 있다.

주요 분석 대상은 다음과 같다.

  • 생산 라인의 병목
  • 기계별 가동률
  • 재공품 수량
  • 작업 배치와 우선순위
  • 장비 고장과 수리
  • 작업자 및 운반 장치 배치
  • 생산량과 납기
  • 완충 공간의 크기
Simulation
begin
    Process class Machine(Name, ProcessingTime);
        text Name;
        long real ProcessingTime;
    begin
        while true do
        begin
            Passivate;

            OutText(Name);
            OutText(" starts processing at ");
            OutFix(Time, 2, 8);
            OutImage;

            Hold(ProcessingTime);

            OutText(Name);
            OutText(" completes processing at ");
            OutFix(Time, 2, 8);
            OutImage
        end
    end;

    ref(Machine) Lathe;

    Lathe :- new Machine("Lathe", 5.0);
    Activate Lathe;

    Hold(20.0)
end;

기계 고장을 확률적으로 발생시키려면 난수 분포와 별도의 고장 프로세스를 결합할 수 있다.

Process class FailureGenerator(MachineProcess);
    ref(Process) MachineProcess;
begin
    integer Seed;

    Seed := 12345;

    while true do
    begin
        Hold(NegExp(100.0, Seed));

        Cancel(MachineProcess);

        OutText("Machine failure at ");
        OutFix(Time, 2, 8);
        OutImage;

        Hold(10.0);

        Reactivate MachineProcess at Time
    end
end;

이러한 모델은 실제 생산 라인을 중단하지 않고 설비 구성과 운영 규칙을 비교하는 데 사용할 수 있다.

교통과 물류

차량, 승객, 화물과 도로망은 독립된 상태와 행동을 가진 객체로 표현하기 쉬우므로 Simula의 초기 모델링 목표와 밀접했다.

교통 및 물류 모델에서는 다음과 같은 문제를 다룰 수 있다.

  • 버스와 열차의 운행 간격
  • 차량과 승객의 대기 시간
  • 교차로와 도로 구간의 혼잡
  • 항만과 창고의 화물 흐름
  • 배송 차량의 배차
  • 적재·하역 설비의 수
  • 항공기 도착과 활주로 사용
  • 운송 경로별 처리량
Simulation
begin
    Process class Bus(RouteName);
        text RouteName;
    begin
        integer StopNumber;

        for StopNumber := 1 step 1 until 5 do
        begin
            OutText(RouteName);
            OutText(" reaches stop ");
            OutInt(StopNumber, 2);
            OutText(" at ");
            OutFix(Time, 2, 8);
            OutImage;

            Hold(10.0)
        end
    end;

    Activate new Bus("Route A");
    Activate new Bus("Route B") delay 5.0;

    Hold(60.0)
end;

각 버스 프로세스는 자신의 현재 정류장과 이동 시간을 보존한다. 승객 프로세스와 정류장 대기열을 추가하면 배차 간격에 따른 승객 대기 시간을 측정할 수 있다.

통신망과 통신 프로토콜

Simula는 전화망, 패킷 교환망과 통신 프로토콜의 동작을 모델링하는 데도 사용되었다. 통신 시스템은 여러 노드와 채널이 동시에 작동하는 것처럼 보이지만, 실제 모델에서는 사건이 발생하는 순서에 따라 상태가 변하기 때문에 이산 사건 시뮬레이션으로 표현하기 적합하다.

모델의 주요 객체는 다음과 같이 구성할 수 있다.

  • 통신 노드
  • 송신기와 수신기
  • 패킷 또는 호출
  • 통신 링크
  • 버퍼와 대기열
  • 전송 오류와 재전송 타이머
  • 라우팅 및 흐름 제어 알고리즘
Simulation
begin
    Process class Packet(Identifier, TransmissionTime);
        value Identifier;
        integer Identifier;
        long real TransmissionTime;
    begin
        OutText("Packet ");
        OutInt(Identifier, 3);
        OutText(" starts transmission at ");
        OutFix(Time, 2, 8);
        OutImage;

        Hold(TransmissionTime);

        OutText("Packet ");
        OutInt(Identifier, 3);
        OutText(" arrives at ");
        OutFix(Time, 2, 8);
        OutImage
    end;

    Activate new Packet(1, 2.0);
    Activate new Packet(2, 4.0) delay 1.0;

    Hold(10.0)
end;

패킷 손실, 링크 점유, 버퍼 한계와 재전송 규칙을 추가하면 프로토콜의 처리량과 지연 시간을 분석할 수 있다. Simula는 실제 통신망 시뮬레이터와 통신 프로토콜 연구에도 활용된 것으로 기록되어 있다.[88]

컴퓨터 시스템과 운영체제 모델링

프로세서, 메모리, 입출력 장치와 작업 스케줄러 역시 사건과 대기열로 구성할 수 있다. Simula는 실제 운영체제를 구현하는 언어로 널리 사용되기보다 운영체제와 컴퓨터 시스템의 동작을 모델링하는 데 활용될 수 있었다.

분석 가능한 항목은 다음과 같다.

  • CPU 스케줄링 정책
  • 디스크와 입출력 대기열
  • 메모리 요청과 작업 교체
  • 다중 사용자 시스템의 응답 시간
  • 배치 작업의 처리량
  • 캐시와 버퍼 정책
  • 병렬 작업의 자원 경합
Simulation
begin
    ref(Head) ReadyQueue;
    Boolean CPUIsBusy;

    Process class ProgramJob(Number, ExecutionTime);
        value Number;
        integer Number;
        long real ExecutionTime;
    begin
        if CPUIsBusy then
            Wait(ReadyQueue);

        CPUIsBusy := true;

        OutText("Job ");
        OutInt(Number, 3);
        OutText(" obtains CPU at ");
        OutFix(Time, 2, 8);
        OutImage;

        Hold(ExecutionTime);

        if ReadyQueue.Empty then
            CPUIsBusy := false
        else
        begin
            ref(Process) NextJob;

            NextJob :-
                ReadyQueue.First qua Process;

            NextJob.Out;
            Activate NextJob
        end
    end;

    ReadyQueue :- new Head;

    Activate new ProgramJob(1, 5.0);
    Activate new ProgramJob(2, 2.0) delay 1.0;
    Activate new ProgramJob(3, 4.0) delay 2.0;

    Hold(20.0)
end;

스케줄링 정책을 바꾸거나 우선순위를 추가하면 평균 대기 시간, 응답 시간과 처리량을 비교할 수 있다.

전자 회로와 VLSI 설계

Simula는 디지털 회로와 VLSI 설계를 시뮬레이션하는 데 사용되었다. 회로의 게이트, 신호선과 소자를 객체로 표현하고, 입력 변화가 일정한 전파 지연 뒤 다른 소자에 전달되는 과정을 사건으로 예약할 수 있다.[89]

Simulation
begin
    Process class SignalChange(
        SignalName,
        NewValue,
        Delay
    );
        text SignalName;
        Boolean NewValue;
        long real Delay;
    begin
        Hold(Delay);

        OutText(SignalName);
        OutText(" becomes ");
        OutText(
            if NewValue then
                "true"
            else
                "false"
        );
        OutText(" at ");
        OutFix(Time, 2, 8);
        OutImage
    end;

    Activate new SignalChange(
        "Output",
        true,
        2.5
    );

    Hold(10.0)
end;

실제 회로 모델에서는 논리 게이트가 입력 신호 객체를 참조하고, 출력 변화가 다음 게이트의 사건을 생성하도록 구성할 수 있다. 객체 지향 구조는 여러 종류의 게이트가 공통 신호 처리 기능을 상속하면서 각자의 논리 연산을 구현하게 한다.

공정 제어

Simula는 공장 설비, 화학 공정과 제어 시스템의 동작을 분석하는 데도 적용되었다. Computer History Museum은 시뮬레이션 이외의 활용 분야로 공정 제어를 명시한다.[90]

공정 제어 모델에서는 센서, 제어기, 밸브, 탱크와 경보를 객체로 만들 수 있다.

class Tank(Capacity);
    value Capacity;
    real Capacity;
begin
    real Level, InputRate, OutputRate;

    procedure Update(Duration);
        value Duration;
        real Duration;
    begin
        Level :=
            Level +
            (InputRate - OutputRate) * Duration;

        if Level < 0.0 then
            Level := 0.0;

        if Level > Capacity then
            Level := Capacity
    end
end;

이 클래스는 연속적인 양을 단순화된 시간 단계로 갱신한다. 사건 기반 경보나 밸브 변경 프로세스를 결합하면 제어 정책에 따른 시스템 반응을 시험할 수 있다.

Simula의 표준 SIMULATION은 이산 사건 모델에 중점을 두므로, 미분 방정식을 직접 풀어야 하는 정밀한 연속 시뮬레이션에는 별도의 수치 계산 절차나 혼합 모델이 필요하다.

과학 및 공학 계산

Simula는 ALGOL 60의 산술 표현식, 배열, 절차와 재귀 기능을 유지했기 때문에 일반적인 과학 계산과 자료 처리에도 사용할 수 있었다. Computer History Museum은 과학 자료 처리를 Simula의 활용 분야 가운데 하나로 기록한다.[91]

begin
    integer Index;
    real Sum, Average;
    real array Measurements(1:100);

    for Index := 1 step 1 until 100 do
        Measurements(Index) :=
            Sin(Index / 10.0);

    for Index := 1 step 1 until 100 do
        Sum := Sum + Measurements(Index);

    Average := Sum / 100;

    OutText("Average: ");
    OutFix(Average, 6, 14);
    OutImage
end;

다만 Simula는 FORTRAN처럼 수치 계산 생태계를 중심으로 발전한 언어는 아니었다. 대규모 선형대수 라이브러리, 고성능 수치 계산 도구와 과학용 패키지의 범위에서는 FORTRAN과 다른 수치 언어가 더 널리 사용되었다. Simula의 강점은 계산 자체보다 계산 대상의 구조와 상호작용을 객체로 모델링하는 데 있었다.

컴퓨터 그래픽스

클래스와 가상 절차는 서로 다른 그래픽 객체를 공통 계층으로 구성하는 데 적합했다. Simula는 컴퓨터 그래픽스 프로그램에도 사용되었으며, Computer History Museum은 그래픽스를 주요 비시뮬레이션 응용 분야로 언급한다.[92]

class GraphicObject;
virtual:
    procedure Draw;
begin
end;

GraphicObject class Circle(X, Y, Radius);
    real X, Y, Radius;
begin
    procedure Draw;
    begin
        OutText("Draw circle at ");
        OutFix(X, 2, 8);
        OutText(", ");
        OutFix(Y, 2, 8);
        OutText(" radius ");
        OutFix(Radius, 2, 8);
        OutImage
    end
end;

GraphicObject class Line(
    X1,
    Y1,
    X2,
    Y2
);
    real X1, Y1, X2, Y2;
begin
    procedure Draw;
    begin
        OutText("Draw line");
        OutImage
    end
end;
ref(GraphicObject) array Scene(1:2);
integer Index;

Scene(1) :- new Circle(10.0, 20.0, 5.0);
Scene(2) :- new Line(0.0, 0.0, 30.0, 40.0);

for Index := 1 step 1 until 2 do
    Scene(Index).Draw;

그래픽 객체는 위치, 크기와 스타일을 상태로 보관하고 Draw를 가상 절차로 제공할 수 있다. 장면은 서로 다른 하위 클래스 객체를 상위 클래스 참조 배열이나 연결 목록으로 관리한다.

이 구조는 이후 객체 지향 그래픽 라이브러리, 사용자 인터페이스 위젯 계층과 장면 그래프에서 널리 사용되는 설계와 유사하다.

조판과 문서 처리

Simula는 조판과 텍스트 처리 프로그램에도 사용된 것으로 기록되어 있다. text 타입은 전체 문자열뿐 아니라 기존 문자 영역의 일부 프레임을 나타낼 수 있어 문서의 토큰, 줄과 단어를 복사하지 않고 다루는 구조를 만들 수 있었다.[93]

class TextLine(Content, MaximumWidth);
    text Content;
    integer MaximumWidth;
begin
    procedure Print;
    begin
        OutText(Content);
        OutImage
    end
end;

문단, 줄, 글꼴과 페이지를 각각 클래스로 정의하고 객체 관계로 문서 구조를 표현할 수 있다. 가상 절차를 사용하면 제목, 본문, 각주와 표가 서로 다른 출력 방식을 제공하도록 만들 수 있다.

알고리즘과 자료구조 연구

Simula의 클래스, 객체 참조와 가비지 컬렉션은 연결 리스트, 트리, 그래프와 같은 동적 자료구조를 구현하는 데 적합했다.

class TreeNode(Value);
    value Value;
    integer Value;
begin
    ref(TreeNode) Left, Right;

    procedure Insert(NewValue);
        value NewValue;
        integer NewValue;
    begin
        if NewValue < Value then
        begin
            if Left == none then
                Left :- new TreeNode(NewValue)
            else
                Left.Insert(NewValue)
        end
        else
        begin
            if Right == none then
                Right :- new TreeNode(NewValue)
            else
                Right.Insert(NewValue)
        end
    end
end;

객체가 다른 객체를 참조할 수 있고 접근 불가능한 객체를 가비지 컬렉터가 회수하므로, 수동 주소 계산 없이 복잡한 자료구조를 구성할 수 있었다.

알고리즘 자체뿐 아니라 알고리즘이 처리하는 요소를 객체로 만들고 가상 절차로 동작을 바꾸는 연구에도 사용할 수 있었다. Simula는 자료 추상화와 객체 기반 설계가 프로그래밍 언어 연구의 주요 주제로 발전하는 데 중요한 실험 환경이 되었다.

소프트웨어 모델링

Simula에서는 프로그램의 클래스 구조를 문제 영역의 개념 구조에 대응시키는 방식이 강조되었다. 이는 실행 결과를 얻기 위한 구현뿐 아니라, 복잡한 시스템을 분석하고 설명하기 위한 모델링 도구로 활용될 수 있다는 의미다.

예를 들어 회사 조직은 직원, 부서와 프로젝트 객체로 표현할 수 있다.

class Department(Name);
    text Name;
begin
    ref(Employee) Manager
end;

class Employee(Name);
    text Name;
begin
    ref(Department) DepartmentReference
end;

객체 참조는 직원과 부서의 관계를 나타내고, 클래스 계층은 관리자, 개발자와 연구원 같은 특수화를 표현할 수 있다.

이러한 관점은 이후 객체 지향 분석 및 설계와 소프트웨어 모델링 방법론으로 이어졌다. Simula 프로그램은 단순한 실행 명령의 집합이 아니라 대상 시스템의 구성 요소와 관계를 나타내는 실행 가능한 모델로 이해되었다.

프로그래밍 언어 연구

Simula는 새로운 언어 개념을 연구하고 검증하는 기반으로도 활용되었다. 클래스, 객체 참조, 접두 클래스, 가상 절차, 코루틴, 가비지 컬렉션과 중첩 클래스가 실제 컴파일러와 프로그램에서 함께 동작했기 때문에 후속 언어 설계에 중요한 실험 사례를 제공했다.

연구 대상에는 다음과 같은 주제가 포함된다.

  • 클래스와 객체의 실행 의미
  • 명목적 하위 타입
  • 동적 디스패치
  • 자료 추상화
  • 블록과 객체 수명
  • 중첩 클래스
  • 코루틴과 준병렬 실행
  • 가비지 컬렉션
  • 별도 컴파일
  • 문제 영역별 언어 확장

Simula의 객체 개념은 Smalltalk, C++, BETA, Eiffel과 여러 후속 언어의 설계에 직접적이거나 간접적인 영향을 주었다. 따라서 Simula는 응용 프로그램 작성 언어인 동시에 객체 지향 언어 설계의 연구 기반이었다.

프로그래밍 교육

Simula는 대학에서 구조적 프로그래밍, 객체 지향 프로그래밍, 자료구조와 시뮬레이션을 가르치는 데 사용되었다. Simula 교재는 언어가 시뮬레이션 전용이라는 인식을 경계하면서, 블록·절차·클래스·가상 속성과 시뮬레이션을 단계적으로 설명했다.[94]

교육에서는 하나의 언어 안에서 다음 내용을 연결해 다룰 수 있었다.

  • 변수, 배열과 제어문
  • 블록 구조와 정적 범위
  • 절차와 재귀
  • 클래스와 객체
  • 상속과 가상 절차
  • 연결 리스트와 트리
  • 코루틴
  • 이산 사건 시뮬레이션
  • 가비지 컬렉션과 객체 수명
begin
    class Student(Name);
        text Name;
    begin
        integer Score;

        procedure AddScore(Value);
            value Value;
            integer Value;
        begin
            Score := Score + Value
        end;

        procedure Print;
        begin
            OutText(Name);
            OutText(": ");
            OutInt(Score, 4);
            OutImage
        end
    end;

    ref(Student) S;

    S :- new Student("Student A");
    S.AddScore(80);
    S.Print
end;

2026년 현재 Simula는 일반적인 입문 프로그래밍 교육의 주류 언어가 아니다. 다만 객체 지향 프로그래밍의 기원, 프로그래밍 언어 역사와 이산 사건 시뮬레이션을 설명하는 교육에서는 원래의 개념을 직접 실행해 볼 수 있는 자료로 활용할 수 있다.

문제 영역별 클래스 환경

Simula의 범용성은 핵심 언어에 모든 응용 기능을 계속 추가하는 방식보다, 문제 영역의 개념을 제공하는 클래스를 접두부로 사용하는 방식에서 드러난다.

Simulation 접두부가 시간과 프로세스를 제공하는 것처럼, 사용자 정의 환경 클래스도 특정 분야의 공통 기능을 제공할 수 있다.

class GeometryEnvironment;
begin
    real Pi;

    class Point(X, Y);
        real X, Y;
    begin
    end;

    real procedure Square(Value);
        value Value;
        real Value;
    begin
        Square := Value * Value
    end;

    Pi := 3.141592653589793;

    inner
end;

GeometryEnvironment
begin
    ref(Point) Center;

    Center :- new Point(10.0, 20.0);

    OutFix(Pi * Square(5.0), 2, 10);
    OutImage
end;

접두 블록은 GeometryEnvironment가 제공하는 Point, PiSquare를 직접 사용할 수 있다. Common Base Language는 이와 같은 구조를 통해 Simula를 응용 분야별 언어로 특화할 수 있다고 설명한다.[95]

이 방식은 현대의 프레임워크, 클래스 라이브러리와 내부 도메인 특화 언어에 가까운 역할을 한다. 다만 Simula의 접두 블록은 단순한 라이브러리 가져오기보다 상위 클래스의 실행 환경과 사용자 프로그램을 직접 결합한다는 차이가 있다.

범용 프로그램 개발

Simula 67은 입출력, 파일, 배열, 텍스트, 클래스와 별도 컴파일을 제공했으므로 시뮬레이션과 무관한 일반 프로그램도 작성할 수 있었다.

begin
    class Record(Name, Value);
        text Name;
        real Value;
    begin
        procedure Print;
        begin
            OutText(Name);
            OutText(": ");
            OutFix(Value, 2, 10);
            OutImage
        end
    end;

    ref(Record) array Records(1:3);
    integer Index;

    Records(1) :- new Record("First", 10.0);
    Records(2) :- new Record("Second", 20.0);
    Records(3) :- new Record("Third", 30.0);

    for Index := 1 step 1 until 3 do
        Records(Index).Print
end;

그러나 Simula의 범용 사용은 당시의 FORTRAN, COBOL과 이후 Pascal·C만큼 넓게 확산되지 않았다. 컴파일러가 주로 대형 컴퓨터와 대학·연구기관에 제공되었고, 구현과 런타임이 비교적 복잡했으며, 객체 지향 기능을 직접 계승한 후속 언어들이 더 넓은 플랫폼과 생태계를 확보했다.

현재의 활용

현재 Simula 자체의 활용은 역사적 전성기와 크게 다르다. 신규 산업 시스템과 일반 응용 프로그램을 Simula로 개발하는 사례는 매우 드물며, 전통적인 활용 분야도 현대 언어와 전문 시뮬레이션 도구가 대부분 대체했다.

2026년 현재 Simula가 현실적으로 사용되는 분야는 다음과 같다.

  • 기존 Simula 소스 코드의 실행과 보존
  • 객체 지향 프로그래밍의 역사 연구
  • 프로그래밍 언어 및 컴파일러 연구
  • Simula의 클래스·코루틴 의미론 실험
  • 이산 사건 시뮬레이션 교육
  • 초기 객체 지향 모델의 교육
  • S-Port와 기존 컴파일러 복원
  • Portable Simula를 이용한 역사적 프로그램 재현

현대의 Portable Simula Revisited를 사용하면 Java가 지원되는 운영체제에서 Simula 프로그램을 작성하고 실행할 수 있다. 따라서 언어는 실행 불가능하게 소멸한 상태는 아니지만, 실제 활용 목적은 새로운 상용 시스템 개발보다 보존과 연구에 가깝다.

현재의 위치와 유산

Simula는 현재 산업 현장에서 사용하는 프로그래밍 언어로서는 사실상 퇴역한 상태다. 새로운 상용 소프트웨어, 기업 정보 시스템, 운영체제, 웹 서비스나 현대적인 시뮬레이션 프로젝트에서 Simula가 기술 스택으로 선택되는 사례는 거의 없으며, 언어를 중심으로 한 상업적 지원, 채용 시장, 패키지 생태계와 대규모 개발자 공동체도 남아 있지 않다.

Simula가 처음 활용되었던 제조, 물류, 통신, 교통과 컴퓨터 시스템 시뮬레이션 분야도 현재는 다른 범용 언어와 전문 시뮬레이션 도구가 대신하고 있다. 프로그래밍 교육에서도 Simula는 20세기 후반까지 일부 북유럽 대학에서 사용되었으나, 이후 Java를 비롯한 현대 언어로 교체되었다.[96]

따라서 현재 Simula의 직접적인 활용은 현업 개발보다 다음과 같은 영역에 한정된다.

  • 객체 지향 프로그래밍의 역사 연구
  • 프로그래밍 언어와 컴파일러 연구
  • 초기 이산 사건 시뮬레이션 모델의 교육
  • 기존 Simula 프로그램과 문서의 보존
  • 과거 컴파일러와 실행 환경의 복원
  • 클래스, 코루틴과 프로세스 상호작용 의미론의 실험

Simula 소스를 현대 컴퓨터에서 실행하는 것은 여전히 가능하다. Java 기반의 Portable Simula Revisited는 현재 운영체제에서 사용할 수 있는 컴파일러와 런타임을 제공하며, 과거 S-Port 구현을 복원한 실행 환경도 함께 개발하고 있다. 그러나 이러한 구현은 Simula를 다시 산업용 주류 언어로 확산시키기 위한 생태계라기보다, 언어와 기존 프로그램을 실행 가능한 형태로 보존하기 위한 프로젝트에 가깝다.[97]

언어 자체의 실무 사용은 끝났지만, Simula에서 형성된 개념은 현대 소프트웨어에 널리 남아 있다.

  • 상태와 동작을 하나의 객체에 결합한다.
  • 공통 속성과 동작을 상위 클래스로 추출한다.
  • 하위 클래스가 기존 클래스를 확장한다.
  • 가상 절차를 통해 실제 객체에 따라 동작을 선택한다.
  • 객체 참조로 실행 중인 시스템의 관계망을 구성한다.
  • 동적으로 생성된 객체의 수명을 가비지 컬렉션으로 관리한다.
  • 프로세스가 실행을 중단하고 이전 위치에서 다시 시작한다.
  • 프로그램 구조를 문제 영역의 개념 구조에 대응시킨다.

이러한 개념은 Smalltalk, C++, Java, C#과 여러 객체 지향 언어에 직접적이거나 간접적으로 계승되었다. 오늘날 클래스, 객체, 상속과 동적 디스패치는 흔한 언어 기능이 되었지만, 이 요소들을 하나의 범용 실행 모델로 처음 결합한 언어는 Simula 67이었다.[98]

Simula가 개척한 프로세스 기반 시뮬레이션 방식도 후속 시뮬레이션 언어와 라이브러리에 흡수되었다. 시뮬레이션 대상을 독립적인 객체나 프로세스로 정의하고, 각 프로세스가 일정 시간 대기한 뒤 중단된 위치에서 실행을 계속하는 구조는 현대의 이산 사건 시뮬레이션에서도 사용된다. 다만 현재 이러한 모델은 Simula 자체보다 Python, Java, C++ 등의 라이브러리와 전문 시뮬레이션 환경에서 구현된다.

따라서 Simula의 현재 가치는 현역 언어로서의 사용량보다 역사적·개념적 영향에 있다. 언어 자체는 산업과 일반 교육에서 퇴역했고, 현재는 연구·교육·소프트웨어 보존 대상으로 남아 있다. 반면 Simula가 확립한 클래스 기반 객체 모델과 실행 가능한 시스템 모델링 방식은 이후 언어와 소프트웨어 설계 전반에 흡수되어 계속 사용되고 있다.

다른 프로그래밍 언어와의 관계

Simula는 ALGOL 60의 문법과 블록 구조를 바탕으로 개발되었으며, SIMSCRIPT를 비롯한 초기 시뮬레이션 언어의 영향을 받았다. 이후 Simula 67이 클래스, 객체, 하위 클래스, 가상 절차와 객체 참조를 하나의 언어 모델로 결합하면서 Smalltalk, C++, BETA, Eiffel을 비롯한 객체 지향 언어의 직접적인 선행 언어가 되었다.

Simula의 영향은 모든 후속 언어에 같은 방식으로 나타나지 않는다. C++은 Simula의 클래스와 가상 절차를 비교적 직접적으로 계승했고, Smalltalk은 클래스와 객체 개념을 받아들이면서 메시지 전송 중심의 새로운 객체 모델로 발전시켰다. Java와 C#은 Simula를 직접 확장한 언어가 아니라 C++, Smalltalk과 이후 객체 지향 언어를 거치며 정착된 클래스 기반 모델을 계승했다.

ALGOL 60

Simula는 ALGOL 60의 직접적인 확장으로 시작되었다. 초기 설계자들은 새로운 언어의 기본 문법과 수치 계산 기능을 처음부터 다시 만들기보다, 당시 체계적인 블록 구조와 절차 문법을 제공하던 ALGOL 60을 기반으로 선택했다.

Simula가 ALGOL 60에서 계승한 주요 요소는 다음과 같다.

  • beginend를 사용하는 블록 구조
  • 정적 이름 범위
  • 지역 변수와 중첩 블록
  • 절차와 재귀 호출
  • if, for, whilegoto
  • 이름 호출과 값 호출 매개변수
  • 배열과 수치 계산 문법
  • 선언부와 실행문의 구분

다음 ALGOL 계열의 블록 구조는 Simula에서도 거의 같은 형태로 사용된다.

begin
    integer Count;
    real Total;

    Count := 10;
    Total := Count * 2.5
end;

Simula의 클래스는 이러한 ALGOL 블록을 여러 번 인스턴스화할 수 있는 이름 있는 패턴으로 일반화한 것으로 볼 수 있다.

class Counter(Start);
    value Start;
    integer Start;
begin
    integer Value;

    procedure Increment;
        Value := Value + 1;

    Value := Start
end;

일반 블록은 프로그램이 해당 위치에 진입할 때 동적 인스턴스를 하나 만들지만, 클래스는 new가 실행될 때마다 별개의 인스턴스를 만든다.

ref(Counter) First, Second;

First :- new Counter(0);
Second :- new Counter(100);

이 과정에서 ALGOL의 지역 변수는 객체의 필드가 되고, 블록 내부 절차는 객체에 속한 절차가 되며, 블록의 동적 인스턴스는 참조 가능한 객체로 발전했다.

Simula는 ALGOL 60의 단순한 문법적 파생 언어가 아니다. ALGOL 블록은 일반적으로 실행이 끝나면 더 이상 사용할 수 없지만, Simula 객체는 클래스 본문 실행이 끝난 뒤에도 참조를 통해 유지될 수 있다. 또한 클래스 계층, 동적 객체 생성, 가상 절차와 가비지 컬렉션은 ALGOL 60에는 존재하지 않았다.

따라서 ALGOL 60과 Simula의 관계는 다음과 같이 정리할 수 있다.

ALGOL 60
└── 블록, 절차, 정적 범위, 제어문
    └── Simula
        ├── 클래스와 객체
        ├── 객체 참조
        ├── 접두 클래스
        ├── 가상 절차
        └── 준병렬 실행

Simula는 ALGOL 60의 구조적 프로그래밍 모델을 폐기하지 않고 객체의 동적 수명과 클래스 계층을 결합했다.[99]

SIMSCRIPT

SIMSCRIPT는 1960년대 초 미국에서 개발된 이산 사건 시뮬레이션 언어다. Simula의 설계자들은 초기 Simula 개발 과정에서 SIMSCRIPT를 비롯한 당시의 시뮬레이션 언어를 조사했으며, 달과 뉘고르의 회고에 따르면 Simula I의 설계는 SIMSCRIPT의 영향을 일부 받았다.[100]

두 언어는 모두 다음과 같은 문제를 다루었다.

  • 시뮬레이션 시간
  • 사건의 예약과 실행
  • 대기 행렬
  • 시스템 구성 요소의 생성과 제거
  • 확률 분포와 난수
  • 통계 수집
  • 이산 사건 시스템의 모델링

그러나 시스템을 표현하는 기본 관점은 달랐다. SIMSCRIPT는 엔티티, 집합과 사건을 중심으로 시뮬레이션 모델을 구성하는 데 중점을 두었다. Simula는 각 구성 요소가 자체 상태와 행동 순서를 가진 프로세스로 동작하는 프로세스 상호작용 방식을 발전시켰다.

Simula에서는 고객의 전체 생명 주기를 하나의 연속된 프로세스로 작성할 수 있다.

Process class Customer;
begin
    OutText("Customer arrives");
    OutImage;

    Wait(Queue);

    OutText("Customer receives service");
    OutImage;

    Hold(5.0);

    OutText("Customer leaves");
    OutImage
end;

사건 중심 언어에서는 도착, 서비스 시작과 서비스 종료가 서로 다른 사건 처리기로 나뉠 수 있지만, Simula에서는 하나의 고객 객체가 중단과 재개를 거치면서 전체 행동을 수행한다.

Simula 67에서는 이러한 프로세스 개념을 범용 객체로 일반화했다. 이에 따라 시뮬레이션 기능은 언어 전체의 고정 문법이 아니라 SIMULATION 시스템 클래스 위에 구축되었다. 이 점은 Simula가 초기 시뮬레이션 언어에서 범용 객체 지향 언어로 전환된 중요한 차이다.

Smalltalk

Smalltalk은 Simula의 객체와 클래스 개념을 직접적으로 계승하면서, 이를 메시지 전송과 대화형 컴퓨팅 환경을 중심으로 재구성한 언어다. Smalltalk 초기 개발에 참여한 인물들의 회고에서도 Smalltalk가 Simula에서 파생된 언어 계보에 속한다는 점이 명시된다.[101]

Simula와 Smalltalk는 다음과 같은 개념을 공유한다.

  • 클래스와 인스턴스
  • 객체의 독립적인 정체성
  • 클래스 계층
  • 상속
  • 동적 메서드 선택
  • 가비지 컬렉션
  • 실행 중 동적으로 생성되는 객체

그러나 두 언어가 객체를 바라보는 방식에는 중요한 차이가 있다.

Simula에서 객체의 절차는 다음과 같이 원격 식별을 통해 호출된다.

ObjectReference.Draw;

Smalltalk에서는 같은 동작을 객체에 메시지를 보내는 방식으로 표현한다.

object draw

Simula는 ALGOL의 절차 호출과 정적 타입 체계를 유지하면서 객체를 도입했다. 반면 Smalltalk는 거의 모든 계산을 객체 사이의 메시지 전송으로 통일했다.

구분SimulaSmalltalk
문법 계보ALGOL 60독자적인 메시지 문법
타입 검사정적 타입 중심동적 타입 중심
기본 실행 모델블록과 절차 호출객체에 대한 메시지 전송
숫자·문자언어의 기본값 타입객체로 표현
클래스 계층접두 클래스클래스와 메타클래스
프로그램 환경컴파일된 프로그램 중심대화형 이미지 환경
동시성 기원코루틴과 프로세스객체·프로세스 환경으로 확장

Smalltalk는 Simula의 클래스 모델을 그대로 복제하지 않았다. Simula에서 상속은 Base class Derived라는 접두 구조로 표현되고, 가상 절차는 virtual: 명세로 선언된다. Smalltalk에서는 하위 클래스가 상위 클래스의 메서드를 이어받고, 메시지를 받은 실제 객체가 실행할 메서드를 동적으로 선택한다.

Simula의 가상 호출은 다음과 같다.

ref(Shape) Item;

Item :- new Circle(5.0);
Item.Draw;

Smalltalk의 메시지 전송은 개념적으로 다음과 같다.

item := Circle radius: 5.
item draw.

Smalltalk는 Simula가 확립한 클래스·객체·상속의 계보를 이어받았지만, 메시지 중심의 순수 객체 환경, 대화형 개발과 그래픽 사용자 환경을 결합하면서 별개의 객체 지향 전통을 형성했다.[102]

C++

C++는 Simula의 영향을 가장 직접적으로 확인할 수 있는 후속 언어 가운데 하나다. 비야네 스트로스트룹은 박사 과정에서 Simula를 사용해 분산 시스템을 시뮬레이션했고, Simula가 복잡한 프로그램을 클래스로 구성하는 데 유용하지만 실행 효율과 시스템 접근에서는 한계가 있다고 판단했다.

그는 1979년부터 C의 효율성과 저수준 제어 능력에 Simula의 클래스 기반 프로그램 조직 기능을 결합한 C with Classes를 개발했다. 이는 이후 C++로 발전했다. 스트로스트룹은 초기 C++의 가장 중요한 영향으로 C와 Simula를 직접 지목했다.[103]

초기 C with Classes가 Simula에서 계승한 주요 요소는 다음과 같다.

  • 클래스
  • 파생 클래스
  • 가상 함수
  • 클래스 계층
  • 생성자
  • 접근 제어
  • 상위 클래스 참조를 통한 다형성
  • 클래스 기반의 프로그램 구조화

Simula의 클래스 계층은 다음과 같이 작성된다.

class Shape;
virtual:
    procedure Draw;
begin
    procedure Draw;
        OutText("Shape")
end;

Shape class Circle(Radius);
    real Radius;
begin
    procedure Draw;
        OutText("Circle")
end;

C++에서는 다음과 같은 구조로 나타난다.

class Shape
{
public:
    virtual void draw()
    {
    }

    virtual ~Shape() = default;
};

class Circle : public Shape
{
public:
    explicit Circle(double radius)
        : radius_(radius)
    {
    }

    void draw() override
    {
    }

private:
    double radius_;
};

Simula의 접두 클래스는 C++의 기반 클래스와 파생 클래스 관계로 이어졌고, 가상 절차는 가상 함수와 동적 디스패치로 이어졌다.

두 언어의 객체 생성 방식도 표면적으로 비슷하다.

ref(Circle) C;
C :- new Circle(5.0);
Circle* c = new Circle(5.0);

그러나 메모리 모델은 크게 다르다. Simula 객체는 참조를 통해 사용되고 가비지 컬렉션으로 관리되는 반면, 초기 C++은 객체의 저장 위치와 수명을 프로그래머가 직접 관리했다. 현대 C++에서도 값 객체, 자동 저장 기간, 스마트 포인터와 수동 메모리 관리가 함께 사용된다.

구분SimulaC++
기반 언어ALGOL 60C
객체 수명가비지 컬렉션결정적 소멸과 자원 관리
객체 표현주로 동적 객체 참조값 객체와 참조 객체 모두 지원
상속단일 접두 클래스단일·다중 상속
다형성가상 절차가상 함수
제네릭직접적인 템플릿 없음템플릿
저수준 접근제한적포인터와 시스템 자원 접근
코루틴객체 실행 모델의 일부오랫동안 별도 기능, 이후 언어 기능 추가

C++은 Simula의 객체 모델을 C의 시스템 프로그래밍 환경에 맞게 재해석했다. Simula가 객체의 안전한 동적 수명과 시스템 모델링을 강조했다면, C++은 클래스 추상화를 사용하면서도 C 수준의 실행 효율과 메모리 제어를 유지하려 했다.

BETA

BETA는 Simula의 설계 계보를 가장 직접적으로 이어받은 언어 가운데 하나다. BETA는 1970년대 후반부터 크리스텐 뉘고르, 벤트 브룬 크리스텐센, 올레 레르스코우 마센과 비르거 묄레르페데르센 등이 참여한 북유럽 프로그래밍 언어 연구에서 개발되었다.

BETA의 개발자들은 Simula가 BETA에 주요한 영향을 주었다고 명시한다. BETA는 Simula의 클래스, 객체, 중첩, 가상 속성과 코루틴 개념을 더 일반적인 패턴이라는 단일 추상화로 통합하려 했다.[104]

Simula에서는 다음 요소가 서로 다른 문법으로 존재한다.

  • 클래스
  • 절차
  • 코루틴
  • 가상 절차
  • 접두 클래스
  • 중첩 클래스

BETA에서는 이들을 패턴이라는 공통 개념으로 표현한다.

Simula
├── class
├── procedure
├── process
├── virtual procedure
└── nested class

BETA
└── pattern

Simula의 상속과 inner는 BETA에서도 중요한 기반이 되었다. BETA는 상위 패턴이 전체 실행 골격을 제공하고 하위 패턴이 inner 위치를 확장하는 구조를 더욱 일반화했다.

Simula의 예는 다음과 같다.

class Document;
begin
    OutText("Before");
    inner;
    OutText("After")
end;

Document class TextDocument;
begin
    OutText("Text")
end;

이러한 실행 구조는 BETA의 패턴 특수화와 중첩 가상 패턴으로 발전했다.

BETA는 Simula보다 훨씬 넓은 통합 추상화 체계를 시도했지만, Simula나 C++보다도 제한적인 연구·교육 생태계에 머물렀다. 그럼에도 Simula 설계자들이 직접 참여한 후속 언어라는 점에서 언어 개념의 연속성을 보여주는 중요한 사례다.

Eiffel

Eiffel은 1980년대에 베르트랑 메이어가 설계한 정적 타입 객체 지향 언어다. Simula와 마찬가지로 클래스, 단일 상속을 중심으로 한 클래스 계층, 가상 호출에 해당하는 동적 바인딩과 가비지 컬렉션을 사용한다.

Eiffel은 Simula의 클래스 기반 전통을 이어받았지만, 언어의 중심 목표를 대규모 소프트웨어 공학과 신뢰성에 두었다. 특히 다음과 같은 기능을 강조했다.

  • 계약에 의한 설계
  • 사전 조건과 사후 조건
  • 클래스 불변식
  • 명시적인 기능 재정의
  • 다중 상속
  • 제네릭 클래스
  • 균일 접근 원칙
  • 자동 메모리 관리

Simula의 클래스는 주로 현실의 시스템 구성 요소와 실행 행동을 모델링하는 데서 출발했다.

class Account(InitialBalance);
    real InitialBalance;
begin
    real Balance;

    procedure Deposit(Amount);
        real Amount;
    begin
        Balance := Balance + Amount
    end;

    Balance := InitialBalance
end;

Eiffel은 같은 계좌 연산에 계약 조건을 언어 수준에서 붙일 수 있다.

deposit (amount: REAL)
    require
        positive_amount: amount > 0.0
    do
        balance := balance + amount
    ensure
        increased: balance = old balance + amount
    end

Eiffel의 클래스 모델은 Simula 이후 정립된 객체 지향 전통에 속하지만, Simula의 코루틴과 시뮬레이션 프로세스 모델을 중심 기능으로 계승하지는 않았다. 대신 클래스 계약과 소프트웨어 정확성에 더 큰 비중을 두었다.

Java

Java는 Simula에서 직접 파생된 문법 언어는 아니지만, Simula에서 시작되어 C++와 Smalltalk 등을 통해 정착한 클래스 기반 객체 모델을 계승한다.

Java와 Simula가 공유하는 주요 특성은 다음과 같다.

  • 클래스와 객체
  • 명목적 클래스 타입
  • 단일 클래스 상속
  • 상위 클래스 참조가 하위 클래스 객체를 가리키는 구조
  • 동적 메서드 디스패치
  • new를 통한 동적 객체 생성
  • 객체 참조
  • 가비지 컬렉션
  • 런타임 타입 검사

Simula의 객체 생성은 다음과 같다.

ref(Vehicle) V;

V :- new Car(4);

Java에서는 다음과 같이 표현된다.

Vehicle vehicle = new Car(4);

가상 호출도 구조적으로 유사하다.

ref(Shape) ShapeReference;

ShapeReference :- new Circle(5.0);
ShapeReference.Draw;
Shape shape = new Circle(5.0);
shape.draw();

그러나 Java가 Simula의 모든 객체 모델을 계승한 것은 아니다.

구분SimulaJava
문법 계보ALGOL 60C·C++ 계열 문법
상속접두 클래스extends
가상 호출virtual:로 지정대부분의 인스턴스 메서드가 기본적으로 동적
현재 객체this ClassNamethis
하향 한정qua명시적 캐스트
타입 분기inspect ... wheninstanceof와 패턴 매칭
값·참조 대입:=:- 분리모두 =
코루틴클래스 객체의 실행 모델초기 Java에는 직접 없음
시뮬레이션표준 SIMULATION 클래스외부 라이브러리 필요
메모리 관리가비지 컬렉션가비지 컬렉션

Java는 Simula의 inner, 중첩된 코루틴 객체와 프로세스 상호작용 모델을 직접 보존하지 않았다. 대신 C++보다 단순한 단일 클래스 상속, 인터페이스와 자동 메모리 관리를 제공했다.

Java를 Simula의 직접 후속판이라고 부르기보다, Simula가 확립한 클래스 기반 객체 모델이 C++와 다른 객체 지향 언어를 거쳐 일반화된 결과를 계승한 언어라고 설명하는 것이 정확하다.

C#

C#도 Simula에서 직접 파생된 언어는 아니며, Java와 C++ 이후의 클래스 기반 객체 지향 전통에 속한다. C#은 클래스, 객체 참조, 단일 클래스 상속, 가상 메서드, 런타임 타입 검사와 가비지 컬렉션 등 Simula에서 시작된 여러 구조를 공유한다.

Shape shape = new Circle(5.0);
shape.Draw();

Simula에서는 가상 절차를 클래스 머리의 virtual: 명세에 등록한다.

class Shape;
virtual:
    procedure Draw;
begin
    procedure Draw;
        OutText("Shape")
end;

C#에서는 상위 클래스의 메서드에 virtual, 하위 클래스에 override를 명시한다.

public class Shape
{
    public virtual void Draw()
    {
    }
}

public class Circle : Shape
{
    public override void Draw()
    {
    }
}

C#은 Simula와 달리 값 타입과 참조 타입을 명시적으로 구분하며, 구조체, 속성, 델리게이트, 이벤트, 제네릭과 비동기 함수 등 .NET 환경을 위한 별도의 기능을 제공한다.

Simula 객체는 기본적으로 참조를 통해 사용되지만, C#에는 클래스 객체와 별도로 값 의미론을 가지는 struct가 존재한다. 또한 C#의 asyncawait는 실행 중단과 재개를 지원하지만, Simula의 Process, Hold와 사건 목록을 직접 계승한 시뮬레이션 모델은 아니다.

Java와 마찬가지로 C#도 Simula의 직접적인 구현 후속 언어가 아니라, Simula 이후 형성된 객체 지향 언어 계보의 후대 구성원으로 보는 편이 적절하다.

Objective-C

Objective-C는 C에 Smalltalk 방식의 메시지 전송을 결합한 언어다. 따라서 Simula와의 관계는 주로 Smalltalk를 통한 간접 계보다.

Objective-C의 객체 호출은 C++이나 Java의 일반적인 가상 함수 호출보다 Smalltalk의 메시지 전송 문법에 가깝다.

[object draw];

Simula에서는 절차 호출 문법을 사용한다.

ObjectReference.Draw;

Objective-C는 Simula에서 시작된 클래스와 상속의 전통을 공유하지만, 메서드 탐색과 메시지 전달 방식은 Smalltalk의 영향을 강하게 받았다. Computer History Museum은 Objective-C가 C에 Smalltalk식 객체 지향 기능을 추가하기 위해 개발되었다고 설명한다.[105]

따라서 계보는 다음과 같이 표현할 수 있다.

Simula
└── Smalltalk
    └── Objective-C

Objective-C는 Simula의 가상 절차 문법이나 프로세스 모델을 직접 계승한 언어가 아니다.

Pascal과 Object Pascal

Pascal은 Simula와 마찬가지로 ALGOL 계열에 속하지만, 초기 Pascal은 Simula의 직접적인 객체 지향 후속 언어가 아니었다. 두 언어는 블록 구조, 정적 타입, 절차와 구조화된 제어문을 공유하지만 서로 다른 목적을 가졌다.

ALGOL 60
├── Simula
│   └── 클래스와 객체 지향 모델
└── Pascal
    └── 구조적 프로그래밍과 교육

초기 Pascal은 레코드, 포인터와 변형 레코드를 제공했지만 클래스와 가상 메서드를 제공하지 않았다. 이후 Object Pascal과 Delphi 계열은 클래스, 상속, 가상 메서드와 객체 참조를 추가하면서 Simula 이후 정착된 클래스 기반 객체 모델을 받아들였다.

Simula의 선언은 다음과 같다.

Shape class Circle;
begin
    procedure Draw;
    begin
    end
end;

Object Pascal에서는 다음과 같은 문법을 사용한다.

type
  TCircle = class(TShape)
  public
    procedure Draw; override;
  end;

Object Pascal은 Pascal의 문법·타입 체계 위에 객체 지향 기능을 추가했다는 점에서, C에 Simula식 클래스 기능을 결합한 초기 C++과 구조적으로 비교할 수 있다. 다만 직접적인 설계 계보와 세부 기능은 구현 및 시기별로 다르다.

CLU

CLU는 1970년대에 바버라 리스코프 등이 개발한 자료 추상화 언어다. Simula와 CLU는 추상화된 프로그램 단위를 제공한다는 공통점이 있지만, 중심 개념에는 차이가 있다.

Simula는 객체의 정체성, 클래스 계층과 상속을 중심으로 한다. CLU는 추상 자료형의 표현을 감추고 명시적인 연산 집합을 제공하는 클러스터를 중심으로 했다.

Simula
└── 객체 + 클래스 계층 + 상속

CLU
└── 추상 자료형 + 숨겨진 표현 + 연산 집합

Simula가 현실의 종류와 하위 종류를 클래스 계층으로 표현했다면, CLU는 자료 표현을 사용하는 코드에서 분리하고 추상 자료형의 명세를 강조했다.

이후 객체 지향 언어들은 두 전통을 함께 받아들였다. 클래스는 Simula식 객체와 상속을 제공하면서, 비공개 필드와 공개 메서드를 통해 CLU식 정보 은닉과 자료 추상화도 제공하게 되었다.

Actor 모델

Actor 모델은 Simula와 마찬가지로 독립적인 계산 주체가 서로 상호작용한다는 관점을 가지지만, 두 실행 모델은 동일하지 않다.

Simula의 프로세스는 하나의 시뮬레이션 실행 안에서 명시적으로 실행권을 넘기는 준병렬 객체다.

Process class Worker;
begin
    Hold(5.0);
    Passivate
end;

Actor는 비동기 메시지를 받아 처리하고 다른 Actor에게 메시지를 보내며, 개념적으로 서로 동시 실행될 수 있다.

Simula Process
├── 공유된 시뮬레이션 시간
├── 사건 목록
├── 협력적 중단과 재개
└── 직접 객체 참조 가능

Actor
├── 비동기 메시지
├── 독립적인 메일박스
├── 개념적인 병렬 실행
└── 메시지를 통한 상태 격리

Simula는 객체 참조를 통해 다른 객체의 속성과 절차에 접근할 수 있지만, Actor 모델은 일반적으로 메시지 전송을 상호작용의 기본 단위로 삼는다. Simula의 프로세스와 코루틴은 Actor 모델의 역사적 배경 가운데 하나로 언급되지만, Simula 자체는 완전한 Actor 언어가 아니다.

현대 객체 지향 언어와의 공통 계보

Simula에서 처음 체계적으로 결합된 다음 구조는 이후 다수의 언어에서 일반적인 기능이 되었다.

  • 클래스가 객체의 공통 구조와 동작을 정의한다.
  • new 또는 이에 대응하는 연산으로 객체를 동적으로 생성한다.
  • 객체는 독립된 정체성을 가진다.
  • 객체 참조를 통해 여러 객체가 연결된다.
  • 하위 클래스는 상위 클래스의 속성과 동작을 이어받는다.
  • 상위 클래스 참조가 하위 클래스 객체를 가리킬 수 있다.
  • 실제 객체의 클래스에 따라 가상 메서드가 선택된다.
  • 도달할 수 없는 객체를 가비지 컬렉션으로 관리할 수 있다.

이러한 공통점만으로 모든 객체 지향 언어가 Simula에서 직접 파생되었다고 볼 수는 없다. 언어마다 Smalltalk, C, Lisp, ML, CLU, Pascal과 운영체제·가상 머신 연구 등 여러 계보를 함께 받아들였다.

Simula의 관계를 단순화하면 다음과 같이 표현할 수 있다.

ALGOL 60 ────────┐
                 ├── Simula
SIMSCRIPT ───────┘    │
                      ├── Smalltalk
                      │    ├── Objective-C
                      │    └── 메시지 중심 객체 언어
                      │
                      ├── C with Classes
                      │    └── C++
                      │         ├── Java에 간접 영향
                      │         └── C#에 간접 영향
                      │
                      ├── BETA
                      ├── Eiffel
                      └── 이후 클래스 기반 객체 언어

이 계보는 정확한 단일 부모·자식 관계를 나타내는 것이 아니라 주요 개념의 전달 경로를 단순화한 것이다. Java와 C#은 C++만으로 만들어진 언어가 아니며, Smalltalk와 다른 언어·가상 머신의 영향도 함께 받았다.

계승되지 않은 요소

Simula의 모든 기능이 후대의 주류 객체 지향 언어에 그대로 계승된 것은 아니다. 다음 요소는 현대 언어에서 사라지거나 별도 기능으로 분리되는 경우가 많다.

  • 값 대입 :=와 참조 대입 :-의 문법적 분리
  • Base class Derived 형식의 접두 클래스
  • 상위 클래스가 하위 클래스 본문을 호출하는 inner
  • this ClassName 형식의 현재 객체 참조
  • 객체 본문 자체가 중단·재개되는 준병렬 실행
  • inspect ... when 연결문
  • qua를 통한 참조 한정
  • 텍스트 프레임과 위치 표시자
  • 프로그램 전체를 클래스 접두부로 확장하는 구조
  • 언어 표준에 포함된 프로세스 기반 시뮬레이션 환경

후대 언어에서는 이러한 기능이 다음과 같이 분리되었다.

Simula 기능후대의 일반적인 대응
inner생성자 연결, super, 템플릿 메서드
준병렬 객체코루틴, 스레드, 비동기 함수
inspect ... when타입 패턴 매칭
qua명시적 캐스트
접두 프로그램 블록프레임워크, 기반 클래스, 실행 환경
SIMULATION외부 시뮬레이션 라이브러리
텍스트 프레임문자열 슬라이스, 뷰
참조 대입 :-일반 대입과 참조 의미론

현대 언어는 Simula 객체가 함께 가지고 있던 데이터, 실행 문맥과 프로세스 역할을 클래스, 코루틴, 스레드, 비동기 함수와 프레임워크 등으로 나누어 제공하는 경우가 많다.

언어 계보에서의 위치

Simula는 ALGOL 계열의 구조적 프로그래밍과 현대 클래스 기반 객체 지향 프로그래밍을 연결하는 위치에 있다. ALGOL 60의 블록과 절차를 유지하면서, 실행 중 생성되는 블록 인스턴스에 정체성·참조·수명과 클래스 계층을 부여했다.

구조적 프로그래밍
ALGOL 60
    │
    ▼
Simula
블록 인스턴스 → 객체
블록 선언     → 클래스
접두 블록     → 상속
가상 절차     → 동적 디스패치
    │
    ▼
클래스 기반 객체 지향 언어

Smalltalk는 이 객체 개념을 메시지 중심 환경으로 발전시켰고, C++은 이를 C의 효율성과 시스템 프로그래밍에 결합했다. BETA는 클래스·절차·코루틴을 패턴으로 통합했으며, Eiffel은 클래스 기반 설계를 계약과 소프트웨어 공학에 적용했다. Java와 C#은 이러한 여러 계보가 정착시킨 클래스, 참조, 상속, 동적 디스패치와 가비지 컬렉션을 현대 가상 머신 환경에 맞게 재구성했다.

따라서 Simula의 중요성은 오늘날 사용되는 언어와 문법이 비슷하다는 데만 있지 않다. 프로그램을 실행 중 상호작용하는 객체의 집합으로 구성하고, 객체의 공통 구조를 클래스 계층으로 정의하며, 상위 타입을 통해 서로 다른 객체의 동작을 호출하는 방식이 실제 범용 언어에서 처음 완성되었다는 데 있다.

영향

Simula의 가장 큰 영향은 시뮬레이션 전용 언어의 기능을 확장한 데 그치지 않고, 프로그램을 서로 관계를 맺고 행동하는 객체들의 집합으로 구성하는 방식을 최초로 체계화했다는 데 있다. Simula 67에서 결합된 클래스, 객체, 하위 클래스, 가상 절차, 동적 객체 생성과 자동 메모리 관리는 이후 객체 지향 프로그래밍의 기본 구성 요소가 되었다.

Simula 자체는 현재 산업 현장에서 사실상 퇴역했지만, 언어가 제시한 객체 모델은 Smalltalk, C++, Java, C#, Eiffel, BETA를 비롯한 후속 언어와 소프트웨어 설계 방법론에 흡수되었다. 오늘날 클래스와 상속을 사용하는 개발자가 Simula 문법을 직접 접할 일은 거의 없지만, 그들이 사용하는 객체 지향 구조의 역사적 뿌리에는 Simula가 있다.[106]

객체 지향 프로그래밍의 형성

Simula 이전에도 레코드, 동적 메모리 할당, 간접 절차 호출과 독립적인 실행 흐름에 해당하는 개념은 존재했다. 그러나 이러한 요소들은 대체로 서로 분리된 기능이었다. Simula 67은 데이터와 절차를 클래스에 결합하고, 클래스로부터 독립적인 정체성과 수명을 가진 객체를 생성하며, 객체의 종류를 클래스 계층으로 조직하는 하나의 통합 모델을 제공했다.

Simula에서 확립된 핵심 구조는 다음과 같다.

  • 클래스는 같은 종류의 객체가 공유하는 데이터와 동작을 정의한다.
  • 객체는 실행 중 동적으로 생성되는 클래스의 개별 인스턴스다.
  • 각 객체는 속성값과 별개의 정체성을 가진다.
  • 객체는 참조를 통해 서로 연결된다.
  • 하위 클래스는 상위 클래스의 속성과 동작을 이어받는다.
  • 가상 절차는 실제 객체의 클래스에 따라 다른 구현을 실행한다.
  • 객체는 참조할 수 있는 동안 유지되며 접근할 수 없게 되면 회수될 수 있다.
  • 객체는 필요한 경우 자신의 실행 상태를 보존할 수 있다.

이 구조는 현대의 클래스 기반 객체 지향 언어에서 일반적으로 볼 수 있는 객체 모델의 기본 형태다. ACM은 Simula가 객체, 클래스, 상속과 가상 속성을 명확하게 확립했고, 데이터·절차·협력하는 행동 순서를 하나의 일반적인 객체 개념으로 통합했다고 평가했다.[107]

Simula의 객체 지향 구조는 단순한 문법적 편의 기능으로 만들어지지 않았다. 달과 뉘고르는 현실의 시스템을 구성하는 사람, 장비, 작업과 자원이 프로그램에서도 대응하는 단위를 갖도록 하려 했다. 객체는 현실의 구성 요소를 나타내고, 클래스는 비슷한 구성 요소의 공통 성질을 나타내며, 객체 사이의 참조는 실제 시스템의 관계를 표현했다.

이 관점은 객체 지향 프로그래밍을 코드 구성 기법이면서 동시에 시스템을 이해하고 표현하는 모델링 방법으로 만들었다.

클래스 개념의 확산

Simula의 가장 직접적인 언어적 유산은 클래스다. 클래스는 단순히 여러 변수를 묶는 레코드가 아니라 다음 요소를 함께 정의했다.

  • 객체가 보유하는 상태
  • 상태를 사용하는 절차
  • 객체 생성 시 실행되는 동작
  • 객체가 속한 타입
  • 상위 클래스와의 관계
  • 하위 클래스가 재정의할 수 있는 가상 동작

Simula의 클래스 선언은 ALGOL 블록을 반복적으로 생성할 수 있는 패턴으로 확장한 것이었다.

class Counter(Start);
    value Start;
    integer Start;
begin
    integer Value;

    procedure Increment;
        Value := Value + 1;

    Value := Start
end;

이후 언어들은 문법과 메모리 모델을 변경하면서도 클래스가 데이터와 동작을 함께 정의한다는 구조를 널리 계승했다.

class Counter
{
public:
    explicit Counter(int start)
        : value_(start)
    {
    }

    void increment()
    {
        ++value_;
    }

private:
    int value_;
};
class Counter
{
    private int value;

    Counter(int start)
    {
        value = start;
    }

    void increment()
    {
        value++;
    }
}

현대 언어의 생성자, 필드, 메서드와 접근 제어 문법은 Simula와 형태가 다르지만, 하나의 선언으로 객체의 공통 구조와 행동을 정의한다는 기본 원리는 이어진다.

상속과 클래스 계층

Simula는 기존 클래스를 새 클래스의 접두부로 사용해 더 구체적인 클래스를 정의했다. 이 구조는 후대에 상속과 파생 클래스라는 이름으로 널리 알려졌다.

class Vehicle;
begin
    real Speed
end;

Vehicle class Car;
begin
    integer Seats
end;

이 선언은 CarVehicle의 속성을 포함하면서 추가 속성을 갖는다는 의미다. 객체의 종류를 일반적인 개념에서 구체적인 개념으로 단계적으로 정의할 수 있었다.

후대 언어에서는 다음과 같은 문법으로 나타난다.

class Car : public Vehicle
{
private:
    int seats_;
};
class Car extends Vehicle
{
    private int seats;
}

Simula는 상속을 코드 복사 기능으로만 취급하지 않았다. 상위 클래스 참조가 하위 클래스 객체를 가리킬 수 있었으므로, 클래스 계층은 타입 호환성과 다형성의 기반이 되었다.

ref(Vehicle) V;

V :- new Car;

이 구조는 오늘날 명목적 하위 타입을 사용하는 다수의 객체 지향 언어에 이어졌다.

다만 상속의 확산은 부작용도 낳았다. 후대의 객체 지향 개발에서는 지나치게 깊은 클래스 계층, 구현 상속에 대한 과도한 의존과 취약한 기반 클래스 문제가 나타났다. 이에 따라 현대 소프트웨어 설계에서는 상속뿐 아니라 인터페이스, 구성과 위임을 함께 사용한다. 이러한 논의도 Simula가 처음 실용화한 클래스 계층이 광범위하게 사용된 결과라고 볼 수 있다.

가상 절차와 동적 디스패치

Simula의 가상 절차는 상위 클래스에 공통 연산을 선언하고 실제 객체의 클래스에 따라 다른 구현을 실행할 수 있게 했다.

class Shape;
virtual:
    procedure Draw;
begin
    procedure Draw;
        OutText("Shape")
end;

Shape class Circle;
begin
    procedure Draw;
        OutText("Circle")
end;
ref(Shape) Item;

Item :- new Circle;
Item.Draw;

Item의 정적 타입은 Shape지만 실제 객체는 Circle이므로 Circle.Draw가 실행된다. 호출자는 객체의 구체적인 클래스를 직접 확인하지 않고 공통 상위 타입을 통해 동작을 요청할 수 있다.

이 방식은 C++의 가상 함수, Java와 C#의 재정의 메서드, Smalltalk의 메시지에 따른 메서드 탐색 등으로 이어졌다. 구체적인 바인딩 규칙은 언어마다 다르지만, 호출자가 객체의 실제 종류를 일일이 분기하지 않고 객체가 자신의 동작을 결정한다는 구조는 객체 지향 다형성의 중심이 되었다.

동적 디스패치는 프로그램을 확장하는 방식에도 영향을 주었다. 새로운 하위 클래스를 추가할 때 기존 호출 코드를 모두 수정하지 않고 새 클래스가 필요한 동작을 제공할 수 있다.

상위 타입을 사용하는 기존 코드
        │
        ├── 기존 하위 클래스
        ├── 새로운 하위 클래스
        └── 이후 추가된 하위 클래스

이 구조는 그래픽 객체, 사용자 인터페이스 위젯, 파일 형식 처리기, 장치 드라이버, 게임 엔티티와 플러그인 시스템 등 다양한 소프트웨어 구조에 사용되었다.

객체의 정체성과 참조

Simula는 객체를 단순한 값 묶음과 구분했다. 객체는 독립적인 정체성을 가지며, 여러 참조가 같은 객체를 가리킬 수 있다.

ref(Node) First, Alias;

First :- new Node;
Alias :- First;

if First == Alias then
    OutText("Same object");

이 모델은 현대의 참조 기반 객체 언어에 널리 계승되었다. 객체를 전달할 때 전체 데이터를 복사하지 않고 객체에 대한 참조를 전달하며, 여러 부분의 프로그램이 같은 객체의 상태를 공유한다.

객체 참조는 연결 리스트, 트리와 그래프뿐 아니라 프로그램 실행 중 존재하는 사용자, 문서, 창, 네트워크 연결과 서비스 객체 사이의 관계를 표현하는 기반이 되었다.

Simula의 객체 참조 모델은 객체 그래프라는 관점을 확산시켰다.

사용자 객체 ── 문서 객체
    │              │
    │              └── 페이지 객체
    │
    └── 세션 객체 ── 네트워크 연결 객체

프로그램의 상태는 개별 변수들의 집합뿐 아니라 실행 중 생성되고 서로 연결된 객체들의 동적 그래프로 이해할 수 있게 되었다.

자동 메모리 관리

Simula는 new로 동적으로 생성된 객체를 명시적인 해제 명령 없이 관리할 수 있도록 가비지 컬렉션을 사용했다. 객체가 다른 객체와 복잡하게 연결되고 순환 참조를 만들 수 있는 환경에서 자동 메모리 관리는 객체 모델을 실용적으로 사용하는 데 중요한 요소였다.

ref(Node) First, Second;

First :- new Node;
Second :- new Node;

First.Next :- Second;
Second.Next :- First;

First :- none;
Second :- none;

외부에서 두 객체에 도달할 수 없다면 런타임은 순환 구조를 포함한 객체들을 회수할 수 있다.

자동 메모리 관리는 Smalltalk, Java, C#, Eiffel과 여러 관리형 언어에서 클래스 기반 객체 모델의 일반적인 구성 요소가 되었다. 객체의 수명을 직접 계산하지 않고 프로그램의 참조 관계를 중심으로 관리할 수 있게 했으며, 대규모 객체 그래프를 다루는 응용 프로그램 개발을 단순화했다.

C++은 Simula의 클래스와 가상 절차를 직접 계승했지만 가비지 컬렉션 대신 결정적 객체 수명과 명시적인 자원 관리를 선택했다. 따라서 Simula의 영향은 모든 후속 언어에서 동일한 메모리 모델로 이어진 것은 아니다.

프레임워크 개념

Simula의 SIMULATION 시스템 클래스는 언어가 제공하는 객체 지향 프레임워크의 초기 사례로 평가된다. Simulation은 프로그램이 사용할 클래스, 프로세스, 시간과 스케줄링 기능을 제공하고, 사용자가 이를 접두부로 삼아 자신의 프로세스 클래스를 정의하게 했다.

Simulation
begin
    Process class Customer;
    begin
        Hold(5.0)
    end;

    Activate new Customer;
    Hold(10.0)
end;

프로그램은 프레임워크가 정한 실행 흐름과 추상화 위에 사용자 객체를 추가한다. 사용자가 전체 스케줄러를 직접 작성하는 대신, Simulation이 사건 목록과 시간 진행을 관리하고 사용자 클래스가 개별 대상의 행동을 정의한다.

ACM의 튜링상 설명은 Simula의 Class Simulation을 초기 객체 지향 프레임워크로 평가한다. 또한 Simula가 추상화 계층을 쌓아 이전 계층이 제공한 개념적·구현적 기반 위에 새로운 계층을 구축할 수 있게 했다고 설명한다.[108]

이 구조는 후대 프레임워크에서 일반화되었다.

  • 프레임워크가 전체 실행 흐름을 관리한다.
  • 사용자는 상속이나 콜백으로 특정 동작을 제공한다.
  • 공통 기반 클래스가 서비스와 상태를 제공한다.
  • 가상 메서드가 사용자 확장 지점이 된다.
  • 프레임워크가 사용자 코드를 호출한다.

이를 흔히 제어의 역전이라고 설명한다. Simula의 inner, 접두 블록과 Simulation 클래스는 이 구조의 초기 형태를 제공했다.

inner와 확장 지점

Simula의 inner는 상위 클래스가 실행 순서의 골격을 정하고, 특정 위치에서 하위 클래스의 동작을 실행하도록 한다.

class DocumentProcessor;
begin
    OpenDocument;
    inner;
    CloseDocument
end;

DocumentProcessor class TextProcessor;
begin
    ProcessText
end;

new TextProcessor의 실행 순서는 다음과 같다.

OpenDocument
ProcessText
CloseDocument

현대 언어에서는 같은 구조가 주로 템플릿 메서드 패턴, 프레임워크 생명 주기 메서드와 콜백으로 표현된다.

Simula의 inner 문법 자체는 주류 후속 언어에 널리 계승되지 않았다. 그러나 상위 추상화가 전체 흐름을 소유하고 하위 구현이 미리 정해진 확장 지점을 채운다는 설계 방식은 사용자 인터페이스 프레임워크, 웹 프레임워크, 게임 엔진과 테스트 프레임워크에서 일반적인 구조가 되었다.

소프트웨어 모델링

Simula는 프로그램 구조를 문제 영역의 구조와 대응시키는 관점에 큰 영향을 주었다. 클래스는 프로그램 구현을 분할하는 단위일 뿐 아니라 현실 또는 분석 대상에서 발견한 개념을 표현하는 단위였다.

예를 들어 교통 시스템은 다음과 같은 개념으로 나눌 수 있다.

교통 시스템
├── 차량
│   ├── 버스
│   └── 트럭
├── 도로
├── 정류장
├── 승객
└── 운행 일정

이 개념들은 클래스와 객체, 하위 클래스와 참조 관계로 대응될 수 있다.

class Road;
begin
    text Name
end;

Process class Vehicle;
begin
    ref(Road) CurrentRoad
end;

Vehicle class Bus;
begin
    integer PassengerCount
end;

이러한 방식은 프로그램을 작성하기 전에 문제 영역의 대상, 속성, 행동과 관계를 식별하는 객체 지향 분석으로 발전했다. 프로그램의 클래스 모델과 분석 대상의 개념 모델을 연결하려는 시도는 객체 지향 설계 방법론, 도메인 모델링과 시각적 모델링 언어에 영향을 주었다.

Simula 설계의 중요한 목표는 계산만 표현하는 것이 아니라 시스템을 기술하는 것이었다. 오슬로 대학교의 달 관련 자료도 뉘고르가 현실 세계의 개념을 모델링할 수 있는 시스템 기술 언어를 핵심 동기로 삼았다고 설명한다.[109]

객체 지향 분석과 설계

Simula 이후 객체 지향 프로그래밍은 단순한 언어 기능을 넘어 소프트웨어 개발 방법론으로 확대되었다. 개발자는 요구사항에서 객체 후보를 찾고, 책임과 관계를 배정하며, 공통 성질을 클래스 계층으로 조직하는 방법을 사용하게 되었다.

Simula에서 비롯된 주요 분석 관점은 다음과 같다.

  • 시스템을 구성하는 지속적인 대상을 식별한다.
  • 각 대상이 보유하는 상태를 찾는다.
  • 대상이 수행하는 동작을 정의한다.
  • 대상 사이의 연결 관계를 나타낸다.
  • 공통 특성을 일반화된 클래스로 추출한다.
  • 시간에 따른 대상의 생명 주기를 기술한다.
  • 프로그램 실행을 객체들의 상호작용으로 이해한다.

이 관점은 1980년대와 1990년대의 객체 지향 분석·설계 방법론, 객체 모델링 기법과 UML이 등장하는 지적 배경의 일부가 되었다.

다만 UML이 Simula에서 직접 만들어진 것은 아니다. UML은 여러 객체 지향 분석·설계 방법을 통합한 후대의 표준이다. Simula의 영향은 클래스, 객체, 일반화와 연관 관계를 중심으로 시스템을 모델링하는 더 근본적인 관점에 있다.

프로그램과 모델의 결합

Simula에서 프로그램은 대상 시스템을 설명하는 모델이면서 동시에 실행 가능한 코드다. 별도의 모델링 언어로 시스템을 설명한 뒤 완전히 다른 구현 언어로 다시 작성하는 대신, 클래스와 프로세스로 작성된 모델을 직접 실행해 결과를 관찰할 수 있었다.

문제 영역 분석
      ↓
클래스와 객체 모델
      ↓
실행 가능한 Simula 프로그램
      ↓
시뮬레이션 결과
      ↓
시스템 설계 변경

이러한 실행 가능한 모델 관점은 시뮬레이션, 모델 기반 개발, 프로토타이핑과 도메인 특화 언어에 영향을 주었다. 모델의 구성 요소와 프로그램 객체가 같은 단위이므로, 모델과 구현 사이의 의미 차이를 줄일 수 있었다.

Simula의 모델링 관점은 특히 시간이 흐르면서 행동하는 시스템을 설명하는 데 강점이 있었다. 객체는 상태만 보유하는 정적인 데이터가 아니라, 생성되고 상호작용하며 대기하고 종료되는 생명 주기를 가진 대상이었다.

이산 사건 시뮬레이션

Simula는 이산 사건 시뮬레이션 분야에도 장기적인 영향을 남겼다. 시뮬레이션 대상을 프로세스로 정의하고, 프로세스가 일정 시간 대기한 뒤 중단된 위치에서 실행을 계속하는 프로세스 상호작용 방식을 체계화했다.

Process class Customer;
begin
    Arrive;
    Wait(ServiceQueue);
    ReceiveService;
    Hold(ServiceTime);
    Leave
end;

이 구조는 고객의 행동을 여러 사건 처리 함수로 분산하지 않고 하나의 연속된 생명 주기로 표현한다.

후대의 프로세스 기반 시뮬레이션 도구에서도 비슷한 구조가 나타난다.

def customer(environment, resource):
    arrival_time = environment.now

    with resource.request() as request:
        yield request
        yield environment.timeout(service_time)

문법과 실행 환경은 다르지만, 독립적인 대상이 자원을 기다리고 시간 지연 뒤 이전 위치에서 실행을 계속한다는 기본 관점은 Simula의 프로세스 모델과 이어진다.

Simula가 사용하던 구체적인 Process, Hold, WaitActivate 문법은 현대 도구에서 그대로 유지되지 않는 경우가 많다. 그러나 프로세스 객체, 사건 목록, 논리적 시간, 자원 대기와 재개라는 구성은 현대 이산 사건 시뮬레이션에서도 널리 사용된다.

코루틴과 협력적 실행

Simula 객체는 자신의 실행 위치를 보존한 채 detach, callresume으로 실행권을 주고받을 수 있었다. 이는 코루틴을 객체 모델과 결합한 초기 사례다.

class Worker;
begin
    FirstStep;
    detach;
    SecondStep;
    detach;
    FinalStep
end;

객체가 중단되면 지역 변수와 실행 위치가 유지되고, 다시 실행될 때 중단 지점부터 이어진다.

후대 언어에서는 코루틴이 제너레이터, 비동기 함수, 경량 스레드와 협력적 태스크 등 여러 형태로 발전했다.

Simula의 준병렬 객체
├── 코루틴
├── 제너레이터
├── 협력적 태스크
├── 비동기 함수
└── 프로세스 기반 시뮬레이션

현대의 모든 코루틴이 Simula에서 직접 파생되었다고 볼 수는 없다. 코루틴 개념 자체에는 Simula 외의 독립적인 연구 계보도 존재한다. Simula의 특징은 코루틴 실행을 클래스 객체의 상태와 생명 주기, 시뮬레이션 프로세스에 결합해 실제 범용 언어 안에서 사용했다는 데 있다.

능동 객체

현대의 많은 객체 지향 언어에서 객체는 메서드가 호출될 때만 동작하는 수동적인 구조다. Simula 객체는 필요에 따라 자체 실행 흐름을 가진 능동 객체가 될 수 있었다.

Process class Machine;
begin
    while true do
    begin
        WaitForJob;
        ProcessJob;
        Hold(ProcessingTime)
    end
end;

각 객체가 독립된 행동 순서를 갖는다는 발상은 능동 객체, Actor, 동시성 객체와 분산 객체 연구에 역사적 배경을 제공했다.

그러나 Simula의 프로세스는 공유된 시뮬레이션 시간과 사건 목록 위에서 협력적으로 실행되며, 일반적인 Actor처럼 비동기 메시지와 독립된 메일박스를 기본 상호작용 방식으로 사용하지 않는다. 따라서 Actor 모델이 Simula와 개념적 연관성을 가지더라도 동일한 실행 모델이라고 할 수는 없다.

Simula의 영향은 객체가 데이터와 호출 가능한 절차만 갖는 것이 아니라, 필요하면 행동의 지속적인 흐름도 가질 수 있다는 가능성을 보여준 데 있다.

객체 지향 프레임워크와 라이브러리

Simula의 클래스 계층은 특정 문제 영역을 위한 재사용 가능한 환경을 구축하는 데 사용되었다. SIMSET은 연결 목록을, SIMULATION은 프로세스와 사건 스케줄링을 제공했다. 사용자는 이를 상속하고 확장해 자신의 모델을 만들었다.

Simula 핵심 언어
    │
    ├── SIMSET
    │     └── 연결 목록과 대기열
    │
    └── SIMULATION
          ├── Process
          ├── 사건 목록
          ├── Time
          └── 스케줄링 연산
                │
                └── 사용자 시뮬레이션 모델

이러한 계층 구조는 범용 언어 위에 문제 영역별 추상화를 구축하고, 사용자가 상속을 통해 필요한 부분을 확장하는 객체 지향 라이브러리 설계의 초기 사례다.

후대의 그래픽 프레임워크, 사용자 인터페이스 툴킷, 네트워크 라이브러리와 응용 프로그램 프레임워크도 기반 클래스, 가상 메서드와 사용자 정의 하위 클래스를 통해 비슷한 확장 구조를 사용했다.

소프트웨어 재사용

Simula의 상속과 클래스는 기존 프로그램 요소를 복사하지 않고 새로운 클래스에서 재사용할 수 있게 했다.

class NamedObject(Name);
    text Name;
begin
    procedure PrintName;
    begin
        OutText(Name);
        OutImage
    end
end;

NamedObject class Customer(Name);
    text Name;
begin
    integer CustomerNumber
end;

Customer는 이름 저장과 출력 기능을 다시 작성하지 않고 NamedObject에서 이어받는다.

이러한 방식은 객체 지향 프로그래밍이 소프트웨어 재사용을 촉진한다는 기대를 형성했다. 공통 기능을 상위 클래스나 프레임워크로 만들고, 응용 프로그램이 이를 특수화하면 개발 비용을 줄일 수 있다고 보았다.

실제로 재사용은 클래스 상속만으로 해결되지 않았으며, 후대에는 구성, 인터페이스, 제네릭 프로그래밍, 모듈과 패키지 관리가 함께 발전했다. 그럼에도 재사용 가능한 클래스 계층과 프레임워크라는 개념은 객체 지향 소프트웨어 산업의 성장에 중요한 역할을 했다.

사용자 인터페이스와 그래픽스 구조

Simula의 객체 모델은 그래픽 객체와 사용자 인터페이스 구성 요소를 표현하는 데 적합했다. 도형, 창, 버튼과 메뉴는 상태와 동작을 가진 객체로 표현할 수 있으며, 공통 기반 클래스를 통해 하나의 집합으로 관리할 수 있다.

class GraphicObject;
virtual:
    procedure Draw;
begin
end;

GraphicObject class Circle;
begin
    procedure Draw;
        DrawCircle
end;

GraphicObject class Line;
begin
    procedure Draw;
        DrawLine
end;

Smalltalk는 이러한 객체 모델을 대화형 그래픽 환경과 결합했고, 이후 Objective-C와 NeXTSTEP, 현대 사용자 인터페이스 프레임워크로 이어지는 계보를 형성했다.

Simula가 현대 GUI 프레임워크를 직접 설계한 것은 아니지만, 서로 다른 화면 요소를 공통 객체 계층으로 관리하고 가상 동작을 호출하는 방식의 기초가 되었다.

객체 지향 데이터베이스와 분산 객체

객체가 정체성, 참조와 클래스 계층을 가진다는 모델은 객체를 프로그램 실행이 끝난 뒤에도 저장하려는 객체 지향 데이터베이스 연구로 확장되었다. 일반 관계형 데이터베이스가 행과 열을 중심으로 데이터를 저장하는 것과 달리, 객체 데이터베이스는 객체의 클래스, 참조 관계와 복합 구조를 보존하려 했다.

실행 중 객체 그래프
        ↓
객체의 영속화
        ↓
저장된 객체 그래프
        ↓
다시 불러온 객체와 참조 관계

분산 객체 시스템도 비슷하게 객체 참조와 메서드 호출을 네트워크를 넘어 확장하려 했다. 원격 객체, 객체 요청 브로커와 분산 컴포넌트 모델은 Simula에서 시작된 객체 정체성·인터페이스·동적 호출의 전통과 연결된다.

다만 객체 데이터베이스와 분산 객체 기술은 네트워크, 데이터베이스와 분산 시스템 연구에서 별도로 발전한 복합적인 결과이므로 Simula의 직접적인 단일 산물로 볼 수는 없다.

객체 지향 프로그래밍 용어

Simula는 오늘날 객체 지향 프로그래밍에서 사용하는 여러 기본 개념을 초기 형태로 정착시켰다.

Simula의 개념후대에 일반화된 용어
class클래스
class object객체 또는 인스턴스
subclass하위 클래스
prefixing상속
virtual procedure가상 메서드
object reference객체 참조
qualification정적 참조 타입
remote identification객체 멤버 접근
qua하향 형변환
inspect ... when실행 중 타입 검사와 패턴 분기
inner상속 계층의 확장 지점
detached object코루틴 또는 중단된 능동 객체

일부 용어는 후대 언어에서 이름과 의미가 달라졌다. Simula의 prefixing은 현대 상속과 밀접하지만 클래스 본문의 실행 결합까지 포함하며, virtual quantity는 가상 절차뿐 아니라 레이블과 다른 속성도 포함할 수 있었다.

따라서 현대 객체 지향 용어를 Simula에 그대로 소급 적용할 때는 세부 의미 차이를 고려해야 한다. 그럼에도 클래스, 객체, 하위 클래스와 가상 동작이라는 핵심 어휘가 Simula에서 실제 언어 기능으로 정착한 사실은 분명하다.

프로그래밍 언어 설계

Simula는 새로운 프로그래밍 패러다임을 기존 언어 위에 점진적으로 구축할 수 있음을 보여주었다. ALGOL 60의 블록과 절차를 유지하면서 블록 인스턴스에 동적 수명과 참조를 부여해 객체로 발전시켰고, 블록 접두 구조를 클래스 상속으로 일반화했다.

ALGOL 블록
    │
    ├── 이름 있는 반복 생성 패턴
    │       └── 클래스
    │
    ├── 실행 중 생성되는 블록 인스턴스
    │       └── 객체
    │
    ├── 외부에서 가리킬 수 있는 인스턴스
    │       └── 객체 참조
    │
    └── 접두 블록
            └── 상속과 클래스 계층

이 설계는 완전히 새로운 문법과 실행 체계를 처음부터 만드는 대신 기존 언어의 구조를 일반화해 새로운 패러다임을 만들었다는 점에서 중요하다.

C++은 비슷한 전략으로 C에 Simula식 클래스 기능을 추가했다. 스트로스트룹은 초기 C++의 주요 영향이 C와 Simula였으며, C의 실행 효율과 Simula의 프로그램 조직 능력을 결합하려 했다고 설명했다.[110]

C++를 통한 산업적 확산

Simula의 객체 모델은 Simula 자체보다 C++를 통해 훨씬 넓은 산업 영역에 확산되었다. C++는 클래스, 파생 클래스와 가상 함수를 시스템 프로그래밍이 가능한 C 계열 환경에 도입했다.

C++는 운영체제 구성 요소, 그래픽스, 사용자 인터페이스, 게임, 통신, 임베디드 시스템과 대규모 응용 프로그램에 사용되면서 클래스 기반 객체 지향 프로그래밍을 산업 전반에 보급했다. Computer History Museum은 C++가 Simula의 고수준 구조와 저수준 언어의 기능을 결합하려는 동기에서 출발했다고 설명한다.[111]

Simula에서 C++로 전달된 주요 요소는 다음과 같다.

  • 클래스
  • 파생 클래스
  • 가상 함수
  • 접근 제어
  • 생성자
  • 클래스 계층을 통한 프로그램 조직
  • 상위 타입을 통한 다형적 호출

C++는 다시 Java, C#, Objective-C++와 여러 언어·프레임워크에 영향을 주었다. 따라서 Simula의 산업적 영향은 직접 사용량보다 후속 언어를 통한 간접 확산에서 훨씬 크게 나타났다.

Smalltalk를 통한 확산

Smalltalk는 Simula의 클래스와 객체 모델을 받아들이면서 모든 계산을 객체에 대한 메시지 전송으로 구성하는 방향으로 발전했다. 초기 Smalltalk 개발자인 아델 골드버그는 프로그래밍 언어 계보의 관점에서 Smalltalk가 Simula에서 파생되었다고 설명했다.[112]

Smalltalk는 다음 영역에 큰 영향을 주었다.

  • 대화형 객체 지향 개발 환경
  • 그래픽 사용자 인터페이스
  • 통합 개발 환경
  • 코드 탐색기와 디버거
  • 메시지 중심 객체 모델
  • 테스트와 리팩터링 문화
  • Objective-C와 동적 객체 언어

따라서 Simula의 객체 개념은 C++를 통해 정적 타입·시스템 프로그래밍 계열로 확산되었고, Smalltalk를 통해 동적 메시지·대화형 개발 환경 계열로 확산되었다고 볼 수 있다.

Simula
├── C++ 계열
│   ├── 정적 타입
│   ├── 시스템 프로그래밍
│   └── Java·C# 등에 간접 영향
│
└── Smalltalk 계열
    ├── 동적 메시지
    ├── 대화형 개발 환경
    └── Objective-C 등에 영향

두 계열은 이후 서로 영향을 주고받았으며 현대 언어에는 양쪽 전통이 혼합되어 있다.

Java와 C#에 남은 구조

Java와 C#은 Simula의 직접적인 문법 후속 언어는 아니지만, Simula에서 시작된 클래스 기반 객체 모델의 주요 요소를 공유한다.

Shape shape = new Circle();
shape.draw();
Shape shape = new Circle();
shape.Draw();

이 코드에는 다음과 같은 Simula의 유산이 포함되어 있다.

  • Shape라는 상위 클래스 타입
  • Circle이라는 하위 클래스
  • new를 통한 동적 객체 생성
  • 상위 타입 변수에 하위 객체를 대입
  • 실제 객체에 따른 동적 메서드 호출
  • 객체 참조와 자동 메모리 관리

Java와 C#은 인터페이스, 제네릭, 예외 처리, 가상 머신과 현대적인 동시성 등 독자적인 기능을 갖추고 있다. 따라서 두 언어를 단순히 현대식 Simula라고 부를 수는 없지만, 객체 모델의 핵심 구조는 Simula가 개척한 전통에 속한다. ACM 역시 Simula의 핵심 개념이 C++, Eiffel, Java와 C# 같은 주류 객체 지향 언어에 포함되었다고 설명한다.[113]

구조적 프로그래밍과의 연결

Simula는 객체 지향 프로그래밍이 구조적 프로그래밍을 완전히 대체한 별개의 흐름이 아니라, ALGOL의 블록 구조 위에서 발전했다는 점을 보여준다.

달은 이후 에츠허르 데이크스트라, C. A. R. 호어와 함께 《Structured Programming》에 참여했고, 호어와 공동으로 계층적 프로그램 구조를 다루었다. ACM의 달 참고 문헌에서도 이 저작은 구조적 프로그래밍의 중요한 문헌으로 평가된다.[114]

Simula는 다음 두 흐름을 연결했다.

따라서 객체 지향 프로그래밍은 구조적 프로그래밍의 원칙을 모두 폐기한 것이 아니라, 블록과 절차를 객체와 클래스라는 더 장기적인 프로그램 구조 안에 배치한 발전으로 볼 수 있다.

교육에 미친 영향

Simula는 북유럽의 대학과 연구기관에서 프로그래밍, 자료구조와 시뮬레이션을 가르치는 데 사용되었다. 학생들은 하나의 언어에서 절차적 프로그래밍, 재귀, 클래스, 객체, 상속, 가상 절차, 연결 구조와 이산 사건 시뮬레이션을 함께 배울 수 있었다.

이 교육 경험은 이후 객체 지향 프로그래밍이 대학 교육 과정에 들어가는 데 기여했다. Simula 자체는 Java와 다른 현대 언어로 교체되었지만, 객체를 먼저 식별하고 클래스로 프로그램을 구성하는 교육 방식은 계속 남았다.

현재 Simula는 일반적인 입문 교육 언어로 사용되지 않지만 다음 주제에서 역사적 교육 자료로 활용된다.

  • 객체 지향 프로그래밍의 기원
  • 클래스와 객체의 초기 의미
  • 상속과 가상 호출의 역사
  • 프로세스 기반 시뮬레이션
  • 코루틴과 실행 상태
  • 프로그래밍 언어 설계의 발전
  • 가비지 컬렉션의 초기 활용

Simula 코드를 직접 실행하면 현대 언어에서 당연하게 여겨지는 기능들이 처음 어떤 문제를 해결하기 위해 만들어졌는지 확인할 수 있다.

연구 공동체에 미친 영향

Simula는 객체 지향 프로그래밍을 독립적인 연구 분야로 형성하는 데 기여했다. 언어 연구는 다음 주제로 확장되었다.

  • 객체와 클래스의 형식적 의미
  • 하위 타입과 타입 호환성
  • 동적 디스패치
  • 정보 은닉과 자료 추상화
  • 다중 상속
  • 제네릭 클래스
  • 객체 수명과 가비지 컬렉션
  • 동시성 객체
  • 객체 데이터베이스
  • 객체 지향 분석과 설계
  • 분산 객체
  • 객체 지향 프레임워크

Simula 자체에는 현대 객체 지향 언어의 모든 기능이 존재하지 않았지만, 이후 연구가 출발할 수 있는 구체적이고 실행 가능한 객체 모델을 제공했다.

Smalltalk, BETA, C++, Eiffel과 CLU 등은 Simula가 제기한 문제를 서로 다른 방향으로 발전시켰다. 일부는 메시지 전송을 강조했고, 일부는 정적 타입과 실행 효율을 강조했으며, 다른 언어는 계약, 패턴과 자료 추상화에 집중했다.

참여적 설계와 시스템 개발

크리스텐 뉘고르의 영향은 프로그래밍 언어 설계에만 한정되지 않는다. 그는 이후 노동자와 실제 사용자가 시스템 개발 과정에 참여해야 한다는 스칸디나비아 시스템 개발 전통과 참여적 설계에도 기여했다.

이 관점은 Simula의 모델링 철학과 연결된다. 시스템을 설계하는 프로그래머가 외부에서 계산 절차만 작성하는 것이 아니라, 실제 현장에서 활동하는 사람과 대상의 개념을 이해하고 프로그램 모델에 반영해야 한다는 생각이다.

ACM은 뉘고르를 객체 지향 프로그래밍의 공동 발명자이면서 참여적 설계와 스칸디나비아 시스템 개발 학파의 선구자로 평가한다.[115]

참여적 설계가 Simula 문법에서 직접 파생된 것은 아니지만, 컴퓨터 시스템을 현실의 조직과 사회적 활동을 표현하고 변화시키는 모델로 본 뉘고르의 연구 관점에서 공통된 배경을 찾을 수 있다.

영향의 한계

Simula가 현대 객체 지향 프로그래밍에 큰 영향을 주었다고 해서 오늘날의 모든 객체 지향 개념을 Simula 하나에서 직접 유래했다고 볼 수는 없다.

다른 중요한 계보에는 다음이 포함된다.

  • Lisp의 동적 데이터와 메타프로그래밍
  • Sketchpad의 그래픽 객체와 제약 관계
  • Smalltalk의 메시지 중심 객체 환경
  • CLU의 자료 추상화와 예외 처리
  • Actor 모델의 비동기 메시지와 동시성
  • ML 계열의 타입 이론
  • 모듈 언어의 정보 은닉
  • 데이터베이스와 분산 시스템 연구

또한 인터페이스, 트레이트, 믹스인, 제네릭, 계약, 메타클래스와 현대 비동기 프로그래밍은 Simula의 직접적인 기능을 단순히 이름만 바꾼 것이 아니다. 여러 언어와 연구 분야에서 별도로 발전한 개념이다.

따라서 Simula의 영향을 정확히 설명하려면 다음 두 극단을 피해야 한다.

  • 현대 객체 지향 프로그래밍의 모든 기능을 Simula가 완성했다고 보는 것
  • Simula가 단지 class라는 키워드만 먼저 사용했다고 축소하는 것

Simula의 실제 공헌은 클래스·객체·상속·가상 절차·참조·동적 생성과 수명 관리를 하나의 범용 언어 모델로 결합하고, 이를 실행 가능한 시스템 모델링에 적용했다는 데 있다.

산업적 사용과 개념적 영향의 차이

Simula는 C++, Java나 Python처럼 장기간 대규모 산업 생태계를 유지한 언어가 아니다. 컴파일러와 런타임은 제한된 플랫폼과 기관을 중심으로 제공되었고, 후속 언어들이 더 넓은 운영체제·도구·라이브러리 생태계를 확보하면서 현업 사용에서 밀려났다.

그러나 언어의 직접 사용량과 개념적 영향은 서로 다르다.

Simula 언어의 직접 사용
1960~1980년대: 연구·교육·시뮬레이션에서 사용
1990년대 이후: 급격히 감소
현재: 보존·교육·연구 중심

Simula 개념의 간접 사용
클래스·객체·상속·동적 호출
→ 후속 언어를 통해 지속적으로 확대
→ 현대 소프트웨어 전반에 정착

Simula 프로그램을 새로 작성하는 개발자는 거의 사라졌지만, Simula가 정립한 객체 모델을 사용하는 개발자는 여전히 매우 많다. 이 차이는 Simula를 역사적으로 중요한 언어로 평가하는 핵심 이유다.

역사적 인정

달과 뉘고르는 Simula I과 Simula 67을 통해 객체 지향 프로그래밍의 출현에 근본적인 아이디어를 제공한 공로로 2001년 튜링상을 공동 수상했다. ACM의 수상 사유는 두 사람이 객체, 클래스, 상속과 가상 속성을 명확히 확립하고 객체 지향 프로그래밍의 등장과 확산을 크게 앞당겼다고 평가한다.[116][117]

두 사람은 Simula 67을 통한 객체 지향 프로그래밍의 기반 개념 도입 공로로 IEEE John von Neumann Medal도 받았다. 이러한 평가는 Simula가 단순한 초기 시뮬레이션 언어가 아니라 프로그래밍 패러다임의 전환점이었다는 점을 보여준다.

오슬로 대학교와 컴퓨터 역사 관련 기관들은 Simula 문서, 컴파일러 자료와 설계자의 논문을 보존하고 있다. Portable Simula Revisited와 복원된 S-Port 구현도 언어를 현대 환경에서 실행 가능한 상태로 유지하고 있다.

현재에 남은 영향

현재 Simula 자체는 현업 프로그래밍 언어로서 사실상 퇴역했다. 그러나 다음과 같은 구조는 현대 소프트웨어에 일상적으로 사용된다.

  • 클래스로 객체의 공통 구조와 동작을 정의한다.
  • 실행 중 객체를 동적으로 생성한다.
  • 객체 참조로 프로그램의 상태와 관계를 구성한다.
  • 상위 클래스 타입으로 여러 하위 객체를 처리한다.
  • 실제 객체의 종류에 따라 메서드를 동적으로 선택한다.
  • 프레임워크가 실행 흐름을 관리하고 사용자 코드가 확장 지점을 구현한다.
  • 객체 그래프의 수명을 가비지 컬렉션으로 관리한다.
  • 문제 영역의 개념을 클래스와 객체 모델로 표현한다.
  • 중단 가능한 프로세스로 시간에 따른 행동을 기술한다.
  • 실행 가능한 프로그램을 시스템 모델로 사용한다.

이러한 기능을 제공하는 모든 언어와 프레임워크가 Simula를 직접 참고한 것은 아니다. 그러나 클래스 기반 객체 지향 프로그래밍의 역사적 계보에서 Simula는 이러한 개념들이 처음 하나의 일관된 범용 언어로 결합된 출발점이다.

Simula의 영향은 언어의 현재 사용자 수보다, 후속 언어와 개발 방법론에 얼마나 깊게 흡수되었는지를 통해 드러난다. Simula는 현역 언어로서는 퇴역했지만, 프로그램을 객체들의 상호작용으로 이해하고 복잡한 시스템을 실행 가능한 객체 모델로 표현하는 방식은 현대 소프트웨어 개발의 기본 관점 가운데 하나로 남아 있다.

장점과 한계

Simula의 장점은 클래스와 객체를 최초로 제공했다는 역사적 사실에만 있지 않다. 현실의 시스템을 구성하는 대상을 프로그램의 객체로 대응시키고, 각 객체의 상태·행동·관계와 시간에 따른 생명 주기를 하나의 실행 모델로 표현할 수 있었다. 특히 클래스, 객체 참조, 상속, 가상 절차, 코루틴과 가비지 컬렉션을 결합한 설계는 당시의 절차적 언어보다 복잡한 시스템을 구조화하는 데 유리했다.[118]

반면 Simula의 언어 체계는 ALGOL 60의 복잡한 문법과 실행 의미를 그대로 상당 부분 유지하면서 객체, 텍스트, 코루틴과 시뮬레이션 기능을 추가했다. 이 때문에 언어를 완전히 이해하려면 블록 구조와 이름 호출뿐 아니라 동적 객체 수명, 접두 클래스, 가상 속성, inner, 객체 한정과 준병렬 실행까지 함께 익혀야 한다.

Simula는 언어 설계와 역사 연구의 관점에서는 중요한 언어이지만, 현재 실무 개발 언어로 선택하기에는 구현, 도구, 라이브러리와 개발자 생태계가 매우 제한적이다. 현대 구현을 통해 실행할 수는 있으나, 산업 현장에서는 사실상 퇴역했으며 주된 가치는 연구·교육과 소프트웨어 보존에 남아 있다.

장점

문제 영역과 프로그램 구조의 대응

Simula는 문제 영역에 존재하는 사람, 장비, 작업, 문서와 자원을 클래스와 객체로 표현할 수 있다. 객체의 속성은 대상의 상태를 나타내고, 클래스 내부 절차는 대상의 행동을 나타내며, 객체 참조는 대상 사이의 관계를 나타낸다.

class Department(Name);
    text Name;
begin
    ref(Employee) Manager
end;

class Employee(Name);
    text Name;
begin
    ref(Department) DepartmentReference
end;

이 구조에서는 부서와 직원이 단순한 배열 요소나 숫자 식별자가 아니라 독립적인 객체로 존재한다. 직원 객체는 자신이 속한 부서 객체를 가리키고, 부서 객체는 관리자를 가리킬 수 있다.

프로그램 구조를 문제 영역의 개념 구조에 대응시키면 대규모 시스템을 전역 변수와 절차의 집합으로만 작성하는 것보다 각 요소의 책임과 관계를 명확하게 표현할 수 있다. 이러한 방식은 이후 객체 지향 분석·설계와 도메인 모델링의 기반이 되었다.

상태와 행동의 결합

클래스는 관련된 데이터와 이를 처리하는 절차를 하나의 선언에 묶는다.

class Account(InitialBalance);
    value InitialBalance;
    real InitialBalance;
begin
    real Balance;

    procedure Deposit(Amount);
        value Amount;
        real Amount;
    begin
        Balance := Balance + Amount
    end;

    Boolean procedure Withdraw(Amount);
        value Amount;
        real Amount;
    begin
        if Amount <= Balance then
        begin
            Balance := Balance - Amount;
            Withdraw := true
        end
        else
            Withdraw := false
    end;

    Balance := InitialBalance
end;

계좌 상태를 사용하는 절차가 계좌 객체 안에 있으므로 호출자가 잔액과 관련 데이터를 매번 별도로 전달할 필요가 없다. 각 객체는 같은 클래스에서 생성되어도 독립적인 상태를 유지한다.

이 결합은 객체가 자신의 상태 변경 규칙을 관리하도록 하며, 후기 Simula 표준의 hiddenprotected를 사용하면 내부 표현에 대한 외부 접근도 제한할 수 있다.

클래스 계층과 재사용

접두 클래스를 사용하면 여러 종류의 객체가 공유하는 속성과 절차를 상위 클래스에 정의할 수 있다.

class Vehicle;
begin
    real Position, Speed;

    procedure Move(Duration);
        value Duration;
        real Duration;
    begin
        Position := Position + Speed * Duration
    end
end;

Vehicle class Bus;
begin
    integer PassengerCount
end;

Vehicle class Truck;
begin
    real CargoWeight
end;

BusTruck은 위치, 속도와 이동 절차를 다시 작성하지 않고 Vehicle에서 이어받는다. 상속은 코드 중복을 줄이는 동시에 버스와 트럭이 모두 차량이라는 개념 관계를 표현한다.

상위 클래스 참조가 하위 클래스 객체를 가리킬 수 있으므로 서로 다른 객체를 공통 타입으로 처리할 수도 있다.

ref(Vehicle) CurrentVehicle;

CurrentVehicle :- new Bus;
CurrentVehicle.Move(10.0);

다형성과 확장성

가상 절차는 공통 호출 구조를 유지하면서 객체 종류에 따라 다른 동작을 실행할 수 있게 한다.

class Shape;
virtual:
    procedure Draw;
begin
    procedure Draw;
        OutText("Shape")
end;

Shape class Circle;
begin
    procedure Draw;
        OutText("Circle")
end;

Shape class Rectangle;
begin
    procedure Draw;
        OutText("Rectangle")
end;
ref(Shape) Item;

Item :- new Circle;
Item.Draw;

Item :- new Rectangle;
Item.Draw;

호출 코드는 객체의 구체적인 클래스를 직접 검사하지 않는다. 새로운 하위 클래스를 추가하더라도 기존 호출부를 변경하지 않고 해당 클래스가 가상 절차 구현을 제공하도록 확장할 수 있다.

이 구조는 그래픽 객체, 시뮬레이션 대상, 사용자 인터페이스 요소와 여러 종류의 작업을 공통 상위 타입으로 처리하는 데 적합하다.

시뮬레이션 대상의 생명 주기 표현

Simula의 Process 객체는 대상의 전체 행동 순서를 하나의 연속된 클래스 본문으로 표현할 수 있다.

Process class Customer;
begin
    Arrive;
    Wait(ServiceQueue);
    ReceiveService;
    Hold(ServiceTime);
    Leave
end;

프로세스는 WaitHold에서 중단되지만 지역 상태와 실행 위치가 유지된다. 다시 활성화되면 중단된 다음 문장부터 실행한다.

사건 중심 프로그램에서는 고객 도착, 서비스 시작과 서비스 종료를 서로 다른 처리기로 분리하고 고객의 현재 상태를 외부에서 관리해야 할 수 있다. Simula의 프로세스 상호작용 모델에서는 하나의 고객 객체가 자신의 전체 생명 주기를 소유한다.

이 방식은 다음과 같은 모델을 읽고 수정하기 쉽게 만든다.

  • 고객의 도착·대기·서비스·퇴장
  • 차량의 출발·이동·정차
  • 기계의 작업·고장·수리
  • 주문의 접수·생산·배송
  • 통신 패킷의 생성·전송·도착

논리적 시간과 효율적인 사건 처리

이산 사건 시뮬레이션에서는 모든 시간 구간을 작은 단위로 반복 계산하지 않고, 상태 변화가 예정된 다음 사건 시점으로 시간을 이동한다.

OutText("Start: ");
OutFix(Time, 2, 8);
OutImage;

Hold(10.0);

OutText("End: ");
OutFix(Time, 2, 8);
OutImage;

Hold(10.0)은 실제 컴퓨터를 10초 동안 멈추는 것이 아니라 현재 프로세스를 시뮬레이션 시각 10만큼 뒤에 다시 예약한다. 그 사이에 실행할 사건이 없으면 논리적 시간은 바로 다음 사건 시각으로 이동한다.

사건 사이에 아무 변화가 없는 시스템에서는 고정된 짧은 시간 간격으로 전체 상태를 반복 검사하는 방식보다 불필요한 계산을 줄일 수 있다.

범용 언어와 시뮬레이션 기능의 분리

Simula 67은 시뮬레이션 전용 문법만으로 구성된 언어가 아니다. 클래스, 객체와 절차를 제공하는 범용 언어 위에 SIMSETSIMULATION 시스템 클래스를 추가했다.

Simula 핵심 언어
├── 클래스와 객체
├── 절차와 배열
├── 텍스트와 파일
├── 코루틴
│
├── SIMSET
│   └── 연결 목록
│
└── SIMULATION
    ├── Process
    ├── 사건 목록
    ├── Time
    └── 스케줄링

시뮬레이션 기능이 필요하지 않은 프로그램은 일반 클래스와 절차만 사용할 수 있다. 다른 문제 영역을 위한 공통 환경도 사용자 정의 접두 클래스로 구성할 수 있다.

Simula Standard는 이러한 특성 때문에 Simula를 특수 응용 언어의 기반으로 사용할 수 있는 범용 언어로 정의한다.[119]

문제 영역별 실행 환경

사용자는 특정 문제 영역에 필요한 클래스와 절차를 하나의 접두 환경으로 정의할 수 있다.

class GeometryEnvironment;
begin
    real Pi;

    class Point(X, Y);
        real X, Y;
    begin
    end;

    Pi := 3.141592653589793;

    inner
end;

GeometryEnvironment
begin
    ref(Point) Position;

    Position :- new Point(10.0, 20.0)
end;

프로그램은 GeometryEnvironment가 제공하는 이름과 실행 환경을 직접 사용한다. 이는 범용 언어 위에 분야별 추상화와 도구를 구축하는 초기 방식이었다.

SIMULATION 역시 같은 구조로 구현되었으므로 언어 기능과 라이브러리 기능 사이의 경계를 유연하게 구성할 수 있었다.

자동 메모리 관리

Simula 객체는 new로 동적으로 생성되며, 일반적인 명시적 객체 해제 연산을 요구하지 않는다.

ref(Node) Current;

Current :- new Node;
Current :- new Node;

첫 번째 객체를 가리키는 다른 참조가 없다면 가비지 컬렉터가 해당 저장 공간을 회수할 수 있다.

객체들이 서로를 참조해 순환 구조를 만들어도 외부에서 도달할 수 없다면 추적 기반 수집으로 회수할 수 있다.

ref(Node) First, Second;

First :- new Node;
Second :- new Node;

First.Next :- Second;
Second.Next :- First;

First :- none;
Second :- none;

이러한 메모리 모델은 대기열, 그래프와 시뮬레이션 객체처럼 수명이 동적으로 바뀌고 관계가 복잡한 대상을 다루는 부담을 줄였다.

값 대입과 참조 대입의 명시적 구분

Simula는 일반 값 대입 :=와 객체 참조 대입 :-를 문법적으로 구분한다.

integer Count;
ref(Customer) First, Second;

Count := 10;

First :- new Customer;
Second :- First;

Second :- First가 객체 내용을 복사하는 것이 아니라 같은 객체를 가리키게 한다는 점이 기호에 드러난다.

텍스트에서도 문자값 복사와 프레임 참조 대입이 구분된다.

text Source, Alias, Copy;

Source :- "SIMULA";
Alias :- Source;

Copy :- Blanks(6);
Copy := Source;

이 구분은 값 복사와 객체 공유를 명시적으로 표현한다는 장점이 있다. 다만 대입 기호가 두 종류라는 점은 학습 부담이 되기도 한다.

정적 타입과 동적 디스패치의 결합

Simula는 클래스 참조의 호환성과 접근 가능한 속성을 정적으로 검사하면서, 가상 절차는 실제 객체의 클래스에 따라 동적으로 선택한다.

ref(Shape) Item;

Item :- new Circle;
Item.Draw;

Item을 통해서는 Shape가 선언한 속성만 사용할 수 있지만, Draw 호출은 Circle의 구현으로 연결된다.

이 구조는 컴파일 단계의 타입 검사와 실행 중 다형성을 함께 제공한다. 관련 없는 클래스 참조 사이의 잘못된 대입을 제한하면서도 상위 타입을 통한 확장 가능한 프로그램 구조를 만들 수 있다.

표준화와 이식성

Simula 67은 여러 기관이 서로 다른 컴퓨터용 컴파일러를 개발하던 시기부터 Common Base와 Simula Standards Group을 통해 언어 호환성을 관리했다. 최종 Simula Standard는 문법·의미·프로세서 적합성 규칙을 정의하고, 프로그램 교환과 이식성을 증진하는 것을 목적으로 명시했다.[120]

표준화된 언어 정의 덕분에 UNIVAC, CDC, IBM과 DEC 계열을 비롯한 서로 다른 시스템에 구현체를 만들 수 있었다. 현대의 Portable Simula도 같은 표준을 기준으로 언어를 Java 환경에 다시 구현한다.[121]

실행 가능한 모델

Simula 프로그램은 대상 시스템을 설명하는 모델이면서 직접 실행할 수 있는 코드다.

현실 시스템 분석
       ↓
클래스와 객체 정의
       ↓
프로세스와 관계 구성
       ↓
모델 직접 실행
       ↓
측정 결과와 설계 변경

별도의 개념 모델을 작성한 뒤 완전히 다른 구현 구조로 다시 옮기지 않고, 객체와 프로세스로 구성한 모델을 실행해 대기 시간, 처리량과 자원 사용률을 측정할 수 있다.

이 특성은 시뮬레이션뿐 아니라 프로토타이핑과 소프트웨어 모델링에서도 유용했다.

한계

복잡한 언어 의미론

Simula는 ALGOL 60의 기존 기능 위에 여러 새로운 실행 모델을 추가했다. 언어 전체를 이해하려면 다음 요소를 함께 알아야 한다.

  • 블록과 정적 범위
  • 이름 호출 매개변수
  • 클래스와 접두 클래스
  • 객체 참조와 클래스 한정
  • 가상 속성
  • inner
  • 중첩 클래스와 외부 객체 문맥
  • 객체의 attached·detached·terminated 상태
  • 코루틴과 준병렬 실행
  • 텍스트 프레임과 위치 표시자
  • 동적 객체 수명과 가비지 컬렉션

각 기능은 독립적으로도 복잡하며 서로 결합될 때 실행 순서를 추론하기 어려울 수 있다.

A class B;
begin
    inner;
    detach
end;

이와 같은 코드를 이해하려면 접두 클래스 본문의 실행 순서, inner가 호출하는 하위 클래스 부분과 detach 이후의 동적 제어 관계를 모두 고려해야 한다.

현대 기준으로 낯선 문법

Simula는 ALGOL 계열의 문법을 사용하므로 C 계열과 현대 언어에 익숙한 개발자에게 낯설 수 있다.

for Index := 1 step 1 until Count do
begin
    Items(Index).Process
end;

특히 다음 문법은 현대의 주류 언어에서 흔하지 않다.

기능Simula 문법
값 대입:=
참조 대입:-
상속Base class Derived
현재 객체this Base
하향 한정Object qua Class
타입 분기inspect ... when
상속 본문 결합inner
주석! ... ;
반복 범위step ... until
이름 호출기본 또는 name 명세

문법 자체가 잘못된 것은 아니지만, 현대 개발자와 도구가 기대하는 관례와 차이가 커서 신규 학습과 도구 개발의 진입 장벽이 된다.

클래스 본문과 상속의 복잡성

Simula의 클래스는 필드와 절차를 정의할 뿐 아니라 본문 자체가 실행된다. 접두 클래스와 하위 클래스의 본문은 inner를 통해 결합된다.

class A;
begin
    BeforeA;
    inner;
    AfterA
end;

A class B;
begin
    BeforeB;
    inner;
    AfterB
end;

B class C;
begin
    ExecuteC
end;

실행 순서는 다음과 같다.

BeforeA
BeforeB
ExecuteC
AfterB
AfterA

이 구조는 프레임워크의 확장 지점을 표현하는 데 강력하지만, 여러 단계의 클래스 계층에서는 전체 실행 흐름이 여러 클래스에 분산된다.

후대의 언어들은 클래스 본문 실행, 생성자, 메서드 재정의와 코루틴을 서로 다른 기능으로 나누는 경우가 많다. 이는 Simula의 통합 모델이 강력한 동시에 복잡했다는 점을 보여준다.

단일 상속

Simula 클래스는 하나의 직접 접두 클래스만 가질 수 있다.

Vehicle class Bus;
begin
end;

두 개 이상의 독립적인 클래스 구현을 직접 함께 상속할 수 없다. 여러 역할을 결합하려면 객체 구성, 참조, 중첩 클래스나 별도의 공통 상위 계층을 사용해야 한다.

단일 상속은 클래스 계층을 비교적 단순하게 유지하는 장점도 있지만, 서로 독립된 여러 기능을 타입에 조합하려는 경우에는 제약이 된다. Simula에는 현대 언어의 인터페이스, 트레이트나 믹스인과 직접 대응하는 일반적인 기능이 없다.

제한적인 접근 제어

초기 Simula 67에서는 클래스 속성의 외부 접근이 기본적으로 상당히 개방되어 있었다. hiddenprotected는 후기 표준에서 추가되었지만, 현대 언어의 단순한 public, private, protected 체계와 의미가 정확히 일치하지 않는다.

class Account;
hidden Balance;
begin
    real Balance
end;

접두 클래스 계층, 가상 속성과 원격 식별을 함께 고려해야 하므로 접근 규칙이 직관적이지 않을 수 있다.

정보 은닉과 공개 인터페이스를 중심으로 클래스를 설계하는 현대적인 관점에서는 Simula의 초기 클래스가 구현 세부 사항을 충분히 강제해서 감추지 못한다는 한계가 있다.

일반적인 제네릭 프로그래밍의 부재

Simula에는 C++ 템플릿, Java 제네릭이나 ML 계열의 매개변수화 다형성과 같은 범용 제네릭 타입 기능이 없다.

정수 목록과 객체 목록처럼 요소 타입만 다른 자료구조를 하나의 타입 안전한 일반 구현으로 작성하기 어렵다. 상위 클래스 참조를 이용한 다형성은 가능하지만, 산술값과 서로 관련 없는 타입까지 일반적으로 매개변수화하지는 못한다.

현대 언어
List<Integer>
List<Customer>
List<Shape>

Simula
요소 타입별 클래스 작성
또는 공통 상위 클래스 참조 사용

이 때문에 범용 컨테이너와 알고리즘 라이브러리를 타입 안전하게 구성하는 능력은 현대 언어보다 제한적이다.

예외 처리 체계의 부재

표준 Simula에는 현대 언어에서 사용하는 구조화된 try, catch, finally 예외 처리와 직접 대응하는 일반적인 기능이 없다.

오류는 다음 방식으로 처리될 수 있다.

  • 반환값과 Boolean 결과
  • 조건문
  • 레이블과 goto
  • 표준 환경의 오류 처리 기능
  • 구현별 확장
  • 별도의 오류 처리 클래스
if Output.Open(Buffer) then
begin
    Output.OutText("Result");
    Output.Close
end
else
    OutText("Unable to open file");

오류가 여러 호출 단계를 거쳐 전달되는 프로그램에서는 반환값을 계속 확인해야 할 수 있다. 외부 자원을 정리하면서 오류를 전달하는 구조도 현대의 예외 처리 및 결정적 자원 관리보다 작성하기 어렵다.

자원 관리의 한계

가비지 컬렉션은 객체 메모리를 자동으로 회수하지만 파일, 네트워크 연결과 장치 같은 외부 자원이 적절한 시점에 닫힌다는 보장은 제공하지 않는다.

ref(OutFile) Output;

Output :- new OutFile("result.txt");

if Output.Open(Blanks(80)) then
begin
    Output.OutText("Result");
    Output.OutImage;
    Output.Close
end;

사용자는 Close를 직접 호출해야 한다. 현대 언어의 using, try-with-resources, defer나 C++의 RAII처럼 범위를 벗어날 때 자원을 자동 정리하는 일반적인 언어 구조가 없다.

오류나 비정상적인 제어 이동이 발생하면 정리 절차 호출을 빠뜨릴 가능성이 있다.

객체와 값의 제한적인 구분

Simula의 클래스 객체는 참조를 통해 사용되며 객체 자체를 일반 값처럼 복사하는 연산은 제공되지 않는다.

ref(Point) A, B;

A :- new Point(1.0, 2.0);
B :- A;

B :- A는 같은 객체를 공유한다. 독립적인 복사본이 필요하면 새 객체를 만들고 각 속성을 직접 복사하는 절차를 작성해야 한다.

B :- new Point(A.X, A.Y);

작고 불변인 자료형이나 수학적 값처럼 값 의미론이 자연스러운 사용자 정의 타입을 표현할 때에도 클래스 참조 모델을 사용해야 한다. 현대 언어의 구조체, 레코드, 데이터 클래스와 값 객체에 비해 선택지가 제한적이다.

참조 안전성 부족

객체 참조는 none, 텍스트 참조는 notext가 될 수 있다.

ref(Customer) Current;

Current :- none;

if Current =/= none then
    Current.Print;

유효성 검사를 하지 않고 원격 속성에 접근하면 실행 오류가 발생한다. Simula에는 현대 언어의 선택 타입, 비널 타입, null 안전 호출이나 정적 null 분석이 없다.

하향 참조 대입과 qua 역시 실제 객체가 대상 클래스와 호환되지 않으면 실행 중 오류를 일으킨다.

ref(Vehicle) V;

V :- new Vehicle;

! 실제 객체가 Car가 아니므로 오류가 발생한다.;
! (V qua Car).Start;

inspect ... when으로 안전하게 검사할 수 있지만, 개발자가 명시적으로 사용해야 한다.

공유 가능한 가변 객체

여러 참조가 같은 객체를 가리킬 수 있으므로 한 부분에서 수행한 변경이 다른 부분에 영향을 줄 수 있다.

ref(Account) First, Second;

First :- new Account(100.0);
Second :- First;

Second.Deposit(50.0);

First를 사용하는 코드에서도 잔액 변경이 관찰된다. 별칭이 많아지면 어떤 코드가 객체 상태를 변경했는지 추적하기 어려울 수 있다.

Simula에는 불변 객체, 읽기 전용 참조와 소유권을 일반적으로 강제하는 현대적인 타입 기능이 없다. 객체 공유가 많은 프로그램에서는 규약과 클래스 설계로 변경 권한을 관리해야 한다.

이름 호출의 복잡성

ALGOL에서 계승한 이름 호출은 호출자의 표현식을 절차 안에서 다시 평가할 수 있는 강력한 기능이다.

real procedure Sum(Index, Expression);
    name Index, Expression;
    integer Index;
    real Expression;
begin
    ...
end;

그러나 실제 매개변수의 평가 시점과 부작용을 추론하기 어려울 수 있으며, 구현도 일반적인 값 전달이나 참조 전달보다 복잡하다.

현대 언어에서는 이름 호출 대신 람다 함수, 클로저, 반복자와 명시적인 지연 평가를 사용하는 경우가 많다. 이러한 방식은 지연되는 계산을 타입과 문법에 더 명확하게 나타낸다.

준병렬 실행과 실제 병렬 실행의 차이

Simula의 코루틴과 Process는 여러 객체가 동시에 진행하는 것처럼 모델링하지만, 기본적으로 하나의 실행 흐름 안에서 명시적으로 제어권을 넘기는 준병렬 구조다.

resume(FirstProcess);
resume(SecondProcess);

이는 다중 코어에서 여러 작업을 실제로 동시에 실행하는 병렬 처리 모델이 아니다. 공유된 사건 목록과 결정적인 논리 시간에는 적합하지만, 현대의 병렬 계산·비동기 입출력·분산 시스템을 직접 표현하기에는 부족하다.

현대 구현이 Java 스레드나 가상 스레드를 내부적으로 사용할 수 있어도 Simula 언어가 규정하는 프로세스 의미는 협력적 시뮬레이션 실행에 기반한다.

구현의 높은 난도

Simula 컴파일러와 런타임은 다음 기능을 함께 구현해야 한다.

  • 중첩 블록과 정적 링크
  • 이름 호출
  • 클래스와 접두 클래스
  • 가상 속성
  • 객체 참조 검사
  • 가비지 컬렉션
  • 부분 텍스트와 위치 상태
  • 중단 가능한 객체 실행
  • 코루틴의 실행 문맥 보존
  • goto와 비지역 제어 흐름
  • 사건 목록과 프로세스 스케줄링

특히 detach, call, resume은 실행 스택 일부를 보존하고 나중에 다시 연결해야 한다. Portable Simula의 설계 자료도 Java 환경에서 Simula를 구현할 때 코루틴, 준병렬 순서 제어와 goto가 주요 구현 과제였다고 설명한다.[122]

복잡한 런타임은 새로운 플랫폼으로 구현체를 이식하는 비용을 높였으며, 작은 언어 공동체에서 여러 현대적 컴파일러를 유지하기 어렵게 했다.

초기 구현의 성능과 자원 비용

동적 객체, 가상 호출, 가비지 컬렉션과 코루틴을 지원하려면 단순한 절차적 언어보다 복잡한 런타임 처리가 필요하다. 특히 초기 컴퓨터에서는 메모리 용량과 처리 성능이 제한적이었기 때문에 Simula 프로그램과 런타임의 비용이 중요한 문제가 되었다.

비야네 스트로스트룹은 Simula의 클래스가 대규모 프로그램을 구조화하는 데 유용하다고 평가했지만, 자신의 분산 시스템 시뮬레이션 경험에서는 실행 효율과 시스템 접근 능력이 충분하지 않았다고 설명했다. 그는 이러한 경험을 바탕으로 Simula의 프로그램 조직 능력을 C의 효율성과 결합하려 했고, 그 결과 C with Classes와 C++가 개발되었다.[123]

다만 모든 Simula 구현과 프로그램이 본질적으로 느렸다고 일반화할 수는 없다. 성능은 대상 컴퓨터, 컴파일러, 가비지 컬렉터와 모델 구조에 따라 달라졌다. 한계는 당시의 저수준 언어와 비교했을 때 고수준 객체·시뮬레이션 기능에 추가적인 구현 비용이 필요했다는 데 있다.

플랫폼 및 구현 간 차이

Simula는 표준화되었지만 파일 이름, 문자 집합, 외부 언어 연결, 정밀도, 그래픽스와 운영체제 기능은 구현에 따라 차이가 날 수 있었다.

ref(OutFile) Output;

Output :- new OutFile("result.txt");

이 파일 이름이 실제 시스템에서 어떻게 해석되는지, 사용할 수 있는 문자와 경로 형식이 무엇인지는 운영체제와 구현체에 영향을 받는다.

초기 UNIVAC·CDC·IBM·DEC 구현의 확장 기능에 의존한 프로그램은 현대 구현에서 그대로 실행되지 않을 수 있다. 표준 Simula만 사용한 소스도 문자 인코딩, 파일 입출력과 부동소수점 결과에서 차이가 나타날 수 있다.

제한적인 표준 라이브러리와 생태계

Simula는 입출력, 텍스트, 파일, 수학 함수, 난수, 연결 목록과 이산 사건 시뮬레이션을 위한 중요한 표준 기능을 제공했다. 그러나 현대 범용 언어와 비교하면 다음과 같은 영역의 라이브러리가 부족하다.

  • 현대 네트워크 프로토콜
  • 웹 서버와 클라이언트
  • 암호화와 보안
  • 데이터베이스 드라이버
  • GUI 및 멀티미디어
  • JSON과 현대 직렬화 형식
  • 클라우드 서비스
  • 병렬·분산 처리
  • 머신러닝과 자료 분석
  • 현대 운영체제 API
  • 패키지 관리

필요한 기능을 직접 구현하거나 다른 언어의 라이브러리와 연결해야 하며, 구현별 외부 인터페이스도 다를 수 있다.

현대 개발 도구의 부족

Simula에는 현대 주류 언어와 같은 대규모 개발 도구 생태계가 없다.

부족하거나 제한적인 요소는 다음과 같다.

  • 널리 사용되는 언어 서버
  • 자동 완성과 실시간 정적 분석
  • 통합 리팩터링
  • 현대적인 디버거 통합
  • 코드 포매터와 린터
  • 의존성 및 패키지 관리자
  • 지속적 통합 서비스의 기본 지원
  • 대규모 테스트 프레임워크
  • 취약점 및 공급망 분석
  • 주요 IDE의 공식 지원

Portable Simula는 편집기, 컴파일러와 실행 환경을 제공하지만, 현대의 Java·C++·Python 개발 환경과 같은 수준의 도구 및 플러그인 생태계를 갖추지는 못했다.[124]

개발자 공동체와 인력의 부재

Simula를 현재 실무에서 사용하는 조직과 개발자가 거의 없기 때문에 다음 문제가 발생한다.

  • 숙련된 개발자를 구하기 어렵다.
  • 최신 학습 자료가 적다.
  • 오류 해결 사례와 공동체 지원이 부족하다.
  • 라이브러리 유지보수자를 찾기 어렵다.
  • 장기간 운영할 신규 시스템의 인력 연속성을 보장하기 어렵다.
  • 채용과 기술 이전 비용이 높다.

언어 자체가 기술적으로 실행 가능하더라도 새로운 산업 프로젝트의 기술 스택으로 선택하기 어려운 가장 현실적인 이유다.

표준 발전의 중단

Simula의 마지막 주요 언어 표준은 1986년에 정리되었으며, 이후 새로운 환경에 맞춘 언어 표준의 기능 확장은 사실상 중단되었다.

그 결과 다음과 같은 현대 언어 기능이 표준에 추가되지 않았다.

  • 제네릭
  • 구조화된 예외 처리
  • 모듈 및 패키지 체계의 현대화
  • Unicode 중심 문자열
  • 람다와 클로저 문법
  • 패턴 매칭의 일반화
  • 비동기 함수
  • 실제 병렬 처리
  • 널 안전성
  • 소유권과 불변성
  • 현대적인 외부 함수 인터페이스

현재 구현체가 자체 확장 기능을 제공할 수는 있지만, 여러 구현이 공유하는 새로운 표준 언어로 발전하지는 않는다.

현업에서의 사실상 퇴역

Simula는 현재 새로운 산업용 소프트웨어의 일반적인 개발 언어가 아니다. 상업적 지원 시장, 채용 시장, 라이브러리 생태계와 신규 프로젝트 기반이 사실상 사라졌다.

현대 구현인 Portable Simula Revisited를 통해 Java가 지원되는 운영체제에서 Simula 프로그램을 컴파일하고 실행할 수는 있다. 그러나 이 프로젝트의 중심 목적은 새로운 산업 생태계를 만드는 것보다 표준 Simula를 현대 환경에서 실행 가능한 상태로 보존하는 데 가깝다.[125]

따라서 현재 Simula는 다음과 같이 평가할 수 있다.

평가 항목현재 상태
객체 지향 언어로서의 역사적 중요성매우 큼
이산 사건 시뮬레이션 역사에서의 중요성매우 큼
언어 연구와 교육 가치높음
기존 프로그램 보존 가능성있음
현대 운영체제에서 실행 가능성있음
신규 산업 프로젝트 적합성매우 낮음
개발자·도구·라이브러리 생태계매우 제한적
표준의 지속적인 발전사실상 중단
상업적 지원과 채용 시장사실상 없음

Simula를 완전히 실행할 수 없는 사어라고 부르는 것은 정확하지 않다. 표준과 문서가 보존되어 있고 현대 구현도 존재한다. 그러나 현업 프로그래밍 언어로서는 사실상 퇴역했으며, 현재 사용 목적은 프로그래밍 언어사 연구, 교육, 기존 코드 보존과 구현 복원에 한정된다.

종합 평가

Simula의 설계는 복잡한 시스템을 객체와 프로세스로 표현하는 데 강력했다. 문제 영역의 대상과 프로그램 구조를 직접 대응시키고, 상태와 행동을 객체에 결합하며, 상속과 가상 절차를 통해 프로그램을 확장할 수 있었다. 표준 시뮬레이션 환경은 논리적 시간과 프로세스 상호작용을 언어의 객체 모델 위에 체계적으로 구축했다.

반면 이러한 강점은 복잡한 언어 의미론과 무거운 구현 부담을 동반했다. ALGOL 문법, 이름 호출, 클래스 본문 실행, inner, 코루틴, 텍스트 프레임과 가비지 컬렉션이 하나의 언어에 결합되어 학습과 컴파일러 구현이 어려웠다. 이후 언어들은 Simula의 클래스·상속·가상 호출을 계승하면서 문법을 단순화하거나, 코루틴·시뮬레이션·자료구조를 별도 라이브러리와 기능으로 분리했다.

현재 Simula의 장점은 새로운 실무 프로젝트를 위한 경쟁력보다 역사적 설계의 완성도와 연구 가치에서 더 크게 드러난다. 언어 자체는 현업에서 퇴역했지만, Simula가 확립한 객체 지향 프로그래밍의 핵심 구조는 후속 언어와 현대 소프트웨어 설계 전반에 흡수되어 남아 있다.

관련 문서

  1. IEEE Engineering and Technology History Wiki: Object-Oriented Programming, 1961–1967
  2. Stein Krogdahl: The Birth of Simula
  3. University of Oslo: The Simula Programming Language
  4. Computer History Museum: The First Reference to Simula in Writing Is Made
  5. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  6. Ole-Johan Dahl: The Birth of Object Orientation: the Simula Languages
  7. Computer History Museum: The First Reference to Simula in Writing Is Made
  8. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  9. Ole-Johan Dahl and Kristen Nygaard: SIMULA—An ALGOL-Based Simulation Language
  10. SIMULA 67 Common Base Language
  11. Kristen Nygaard and Ole-Johan Dahl: The Development of the SIMULA Languages
  12. Ole-Johan Dahl: The Birth of Object Orientation: the Simula Languages
  13. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  14. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  15. Kristen Nygaard: The Development of the SIMULA Languages
  16. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  17. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  18. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  19. SIMULA 67 Common Base Language
  20. Ole-Johan Dahl and Kristen Nygaard: SIMULA—An ALGOL-Based Simulation Language
  21. SIMULA 67 Common Base Language
  22. SIMULA 67 Common Base Language
  23. Ole-Johan Dahl and Kristen Nygaard: Class and Subclass Declarations, cited in The Development of the SIMULA Languages
  24. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  25. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  26. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  27. SIMULA 67 Common Base Language
  28. SIMULA 67 Common Base Language, revised 1970 edition
  29. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  30. Computer History Museum: Software & Languages, 1965
  31. Ole-Johan Dahl, Bjørn Myhrhaug and Kristen Nygaard: SIMULA Common Base Language
  32. Ole-Johan Dahl: The Birth of Object Orientation: The Simula Languages
  33. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  34. Stein Krogdahl: The Birth of Simula
  35. Ole-Johan Dahl and Kristen Nygaard: SIMULA—An ALGOL-Based Simulation Language
  36. Some Features of the SIMULA 67 Language
  37. Ole-Johan Dahl: The Birth of Object Orientation—The Simula Languages
  38. Ole-Johan Dahl, Bjørn Myhrhaug and Kristen Nygaard: SIMULA Common Base Language
  39. Robert Pooley: An Introduction to Programming in Simula, Chapter 19
  40. Robert Pooley: An Introduction to Programming in Simula, Class Simulation
  41. Ole-Johan Dahl and Kristen Nygaard: SIMULA—An ALGOL-Based Simulation Language
  42. SIMULA Standard: Class SIMULATION
  43. Robert Pooley: An Introduction to Programming in Simula, EventNotice and EvTime
  44. Robert Pooley: An Introduction to Programming in Simula, Chapter 19
  45. Robert Pooley: An Introduction to Programming in Simula, Scheduling Processes
  46. SIMULA Standard: Activation statements
  47. Robert Pooley: An Introduction to Programming in Simula, Wait and queuing
  48. Robert Pooley: An Introduction to Programming in Simula, Environment statistical procedures
  49. Ole-Johan Dahl: The Birth of Object Orientation—The Simula Languages
  50. Ole-Johan Dahl: The Birth of Object Orientation—The Simula Languages
  51. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  52. SIMULA Common Base Language
  53. SIMULA Common Base Language: Virtual quantities
  54. SIMULA Common Base Language: Object generation and garbage collection
  55. Stein Krogdahl: Concepts and Terminology in the Simula Programming Language
  56. Computer History Museum: Software and Languages, 1965
  57. Ole-Johan Dahl, Bjørn Myhrhaug and Kristen Nygaard: SIMULA Common Base Language
  58. GNU Cim
  59. GNU Cim
  60. Norwegian Computing Center: SIMULA 67 Common Base Language, revised standard
  61. GNU Cim: Compiling
  62. Ole-Johan Dahl, Bjørn Myhrhaug and Kristen Nygaard: SIMULA Common Base Language
  63. SIMULA Common Base Language: Assignment statements
  64. SIMULA Common Base Language: Arithmetic value assignment
  65. SIMULA Common Base Language: Character values and collating sequence
  66. SIMULA Common Base Language: Text reference and text value assignment
  67. SIMULA Common Base Language: Reference relations
  68. SIMULA Common Base Language: Value and reference assignment
  69. SIMULA Common Base Language: Object reference assignment
  70. SIMULA Common Base Language: Object relations
  71. SIMULA Common Base Language: Blocks and dynamic scopes
  72. SIMULA Common Base Language: Dynamic scope and deletion of block instances
  73. SIMULA Common Base Language: Garbage collection and effective object lifetime
  74. SIMULA Common Base Language: Lifetime of arrays and text objects
  75. SIMULA Common Base Language: Value assignment and reference assignment
  76. University of Oslo: SIMULA Standard, 25 August 1986
  77. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  78. SIMULA Standard
  79. Portable Simula Revisited: Programmer's Reference Manual
  80. Portable Simula Revisited: The S-PORT project
  81. GNU Cim source repository
  82. Portable Simula Revisited: Download and installation
  83. Portable Simula Revisited
  84. Portable Simula Revisited: S-Port Simula reconstructed
  85. Ole-Johan Dahl and Kristen Nygaard: SIMULA—An ALGOL-Based Simulation Language
  86. SIMULA Common Base Language: Application language capability
  87. Computer History Museum: Software and Languages, 1965
  88. Stein Krogdahl: Simula and Java—Two of a Kind
  89. Stein Krogdahl: Simula and Java—Two of a Kind
  90. Computer History Museum: Software and Languages, 1965
  91. Computer History Museum: 1965 Timeline
  92. Computer History Museum: Software and Languages
  93. Stein Krogdahl: Simula and Java—Two of a Kind
  94. Robert Pooley: An Introduction to Programming in Simula
  95. SIMULA Common Base Language: Application language capability
  96. Yngve Sundblad: Mother Tongue for a Generation of Nordic Programmers
  97. Portable Simula Revisited
  98. Andrew P. Black: Object-Oriented Programming—Some History, and Challenges for the Next Fifty Years
  99. Ole-Johan Dahl and Kristen Nygaard: SIMULA—An ALGOL-Based Simulation Language
  100. Ole-Johan Dahl and Kristen Nygaard: The Development of the SIMULA Languages
  101. Computer History Museum: Oral History of Adele Goldberg
  102. Daniel H. H. Ingalls: The Smalltalk-76 Programming System—Design and Implementation
  103. Bjarne Stroustrup: A History of C++—1979–1991
  104. Bent Bruun Kristensen et al.: The When, Why and Why Not of the BETA Programming Language
  105. Computer History Museum: The Deep History of Your Apps
  106. ACM A.M. Turing Award: Ole-Johan Dahl
  107. ACM A.M. Turing Award: Kristen Nygaard
  108. ACM A.M. Turing Award: Ole-Johan Dahl
  109. University of Oslo: Ole-Johan Dahl—Scientific Activity
  110. Bjarne Stroustrup: A History of C++—1979–1991
  111. Computer History Museum: 1985 Timeline
  112. Computer History Museum: Oral History of Adele Goldberg
  113. ACM A.M. Turing Award: Kristen Nygaard
  114. ACM A.M. Turing Award: Ole-Johan Dahl Bibliography
  115. ACM A.M. Turing Award: Kristen Nygaard
  116. ACM A.M. Turing Award: Ole-Johan Dahl
  117. ACM A.M. Turing Award: Kristen Nygaard
  118. ACM A.M. Turing Award: Ole-Johan Dahl
  119. SIMULA Standard
  120. SIMULA Standard
  121. Portable Simula Revisited Programmer's Reference
  122. Re-inventing Simula Using Java
  123. Bjarne Stroustrup: A History of C++—1979–1991
  124. Portable Simula Revisited
  125. Portable Simula Revisited Programmer's Reference