프로그래밍 언어 입력에 관한 교육 프로그램입니다. 프로그래밍의 기본 원리: 정적 및 동적 타이핑 강한 타이핑과 약한 타이핑

OO 접근 방식의 타이핑의 단순성은 객체 컴퓨팅 모델의 단순성의 결과입니다. 세부 사항을 생략하면 객체 지향 시스템을 실행할 때 한 종류의 이벤트, 즉 기능 호출만 발생한다고 말할 수 있습니다.


작업을 수행한다는 의미 에프부착된 물체 위에 엑스, 인수 전달 포함 인수(인수가 여러 개이거나 전혀 없을 수도 있음) 스몰토크 프로그래머들은 이 경우 "객체 전달"이라고 말합니다. 엑스메시지 에프논쟁을 가지고 인수"라고 말하지만 이는 용어상의 차이일 뿐이므로 별 의미가 없습니다.

모든 것이 이 기본 구성을 기반으로 한다는 것은 OO 아이디어의 미의식 중 일부를 설명합니다.

기본 설계에서 실행 중에 발생할 수 있는 비정상적인 상황을 따르십시오.

정의: 유형 위반

런타임 유형 위반, 간단히 말해서 유형 위반은 호출 시 발생합니다. x.f(인수), 어디 엑스객체에 부착 O.B.J., 다음 중 하나인 경우:

[엑스].해당하는 구성요소가 없습니다. 에프그리고 적용 가능 O.B.J.,

[엑스].그러한 구성 요소가 존재하지만 인수는 인수그에게는 받아들일 수 없는 일이다.

타이핑의 문제점은 다음과 같은 상황을 피하는 것입니다.

OO 시스템 타이핑 문제

객체 지향 시스템을 실행할 때 유형 위반이 발생할 수 있다는 것을 언제 발견합니까?

핵심 단어는 언제. 조만간 유형 위반이 있음을 깨닫게 될 것입니다. 예를 들어 Employee 개체에서 Launch Torpedo 구성 요소를 실행하려고 하면 작동하지 않으며 실패하게 됩니다. 그러나 나중에보다는 빨리 버그를 찾는 것이 더 나을 수도 있습니다.

정적 및 동적 타이핑

중간 옵션도 가능하지만 다음과 같은 두 가지 주요 접근 방식이 있습니다.

[엑스]. 동적 타이핑: 각 통화가 완료될 때까지 기다린 후 결정을 내리세요.

[엑스]. 정적 타이핑: 일련의 규칙이 주어지면 실행 중에 유형 위반이 가능한지 소스 텍스트에서 결정합니다. 규칙에 따라 오류가 없음이 보장되는 경우 시스템이 실행됩니다.

이러한 용어는 설명하기 쉽습니다. 동적 타이핑을 사용하면 시스템이 실행되는 동안(동적으로) 유형 검사가 수행되는 반면, 정적 타이핑을 사용하면 텍스트에 대해 정적으로(실행 전) 유형 검사가 수행됩니다.

정적 타이핑은 가정합니다 자동 확인, 원칙적으로 컴파일러에 할당됩니다. 결과적으로 우리는 다음과 같은 간단한 정의를 얻었습니다.

정의: 정적으로 유형이 지정된 언어

객체 지향 언어는 시스템 실행이 유형 위반으로 이어지지 않도록 보장하는 일관되고 컴파일러 검사 규칙 세트와 함께 제공되는 경우 정적으로 유형이 지정됩니다.

용어 " 강한입력 중" ( 강한). 이는 유형 위반이 전혀 없어야 하는 정의의 최후통첩 성격에 해당합니다. 또한 가능하다 약한 (약한) 규칙이 특정 위반을 완전히 제거하지 않고 제거하는 정적 타이핑 형태. 이런 의미에서 일부 객체 지향 언어는 정적으로 약한 형식입니다. 우리는 가장 강력한 유형화를 위해 싸울 것입니다.

유형이 지정되지 않은 언어로 알려진 동적 유형 언어에는 유형 선언이 없으며 엔터티는 런타임에 어떤 값이라도 첨부할 수 있습니다. 정적 유형 검사는 불가능합니다.

타이핑 규칙

우리의 OO 표기법은 정적으로 입력됩니다. 타입 규칙은 이전 강의에서 소개되었으며 세 가지 간단한 요구 사항으로 요약됩니다.

[엑스].각 엔터티나 함수를 선언할 때 해당 유형을 지정해야 합니다. 예를 들면 다음과 같습니다. 계정: 계정. 각 루틴에는 0개 이상의 형식 인수가 있으며 형식을 지정해야 합니다. 예: 넣다(x:G;i:INTEGER).

[엑스].어떤 임무에도 x:=y그리고 서브루틴에 대한 호출의 경우 와이형식적 논증에 대한 실제 논증이다 엑스, 소스 유형 와이대상 유형과 호환되어야 합니다. 엑스. 호환성의 정의는 상속을 기반으로 합니다. 호환 가능 , 하위 항목인 경우 일반 매개변수에 대한 규칙으로 보완됩니다(강의 14 참조).

[엑스].부르다 x.f(인수)요구한다 에프대상 유형의 기본 클래스 구성 요소였습니다. 엑스, 그리고 에프호출이 나타나는 클래스로 내보내야 합니다(14.3 참조).

실재론

정적으로 유형이 지정된 언어의 정의는 매우 정확하게 제공되지만 그것만으로는 충분하지 않습니다. 유형 지정 규칙을 만들 때 비공식 기준이 필요합니다. 두 가지 극단적인 경우를 생각해 보겠습니다.

[엑스]. 절대적으로 올바른 언어, 구문적으로 올바른 모든 시스템은 유형도 정확합니다. 유형 선언 규칙은 필요하지 않습니다. 이러한 언어가 존재합니다(정수를 더하고 빼는 폴란드 표기법을 상상해 보세요). 불행하게도 이 기준을 충족하는 실제 보편적 언어는 없습니다.

[엑스]. 완전히 잘못된 언어는 기존 언어를 선택하고 다음을 만드는 입력 규칙을 추가하여 쉽게 만들 수 있습니다. 어느시스템이 잘못되었습니다. 정의에 따르면 언어는 유형이 지정됩니다. 규칙을 따르는 시스템이 없기 때문에 어떤 시스템도 유형 위반을 일으키지 않습니다.

우리는 첫 번째 유형의 언어라고 말할 수 있습니다 적합한, 하지만 쓸모 없는, 후자가 유용할 수 있지만 적합하지 않을 수 있습니다.

실제로 우리는 사용 가능하고 유용한 타입 시스템이 필요합니다. 즉, 계산 요구 사항을 충족할 만큼 강력하고 타이핑 규칙을 충족시키기 위해 복잡하게 만들지 않을 만큼 충분히 편리한 타입 시스템이 필요합니다.

그 언어를 말해보자 현실적인, 실제로 사용 가능하고 유용한 경우. 다음 질문에 대한 범주형 답변을 제공하는 정적 유형 지정의 정의와는 대조적입니다. 언어 X는 정적으로 유형이 지정됩니까?", 사실주의의 정의는 부분적으로 주관적입니다.

이번 강의에서는 우리가 제안하는 표기법이 현실적인지 확인해 보겠습니다.

염세주의

정적 타이핑은 본질적으로 "비관적" 정책으로 이어집니다. 이를 보장하려는 시도 모든 계산이 실패로 이어지지는 않습니다, 거부 오류 없이 끝날 수 있는 계산.

다양한 유형의 일반적이고 비객관적이며 파스칼과 유사한 언어를 고려하세요. 진짜그리고 정수. 기술할 때 n:INTEGER; r:리얼운영자 n:=r규정 위반으로 거절됩니다. 따라서 컴파일러는 다음 명령문을 모두 거부합니다.


실행을 허용하면 [A]가 항상 작동한다는 것을 알 수 있습니다. 모든 숫자 시스템에는 명확하게 0 정수로 변환되는 실수 0,0의 정확한 표현이 있기 때문입니다. [B]도 거의 확실하게 작동할 것입니다. 작업 [C]의 결과는 명확하지 않습니다(소수 부분을 반올림하거나 버려서 결과를 얻고 싶습니까?). [D]는 운영자처럼 작업을 수행합니다.


n^2인 경우< 0 then n:= 3.67 end [E]

여기에는 도달할 수 없는 할당( n^2숫자의 제곱이다 N). 교체 후 n^2~에 N일련의 실행만이 올바른 결과를 제공합니다. 과제 N전체적으로 표현할 수 없는 큰 실제 가치는 실패로 이어질 것입니다.

형식화된 언어에서는 이러한 모든 예제(작동, 작동하지 않음, 때로는 작동함)가 유형 설명 규칙을 위반하는 것으로 간주되어 모든 컴파일러에서 거부됩니다.

질문은 그렇지 않습니다. 우리는 할 것이다우리가 비관주의자인지는 모르겠지만 사실은 얼마나 많이우리는 비관적일 여유가 있습니다. 현실성 요구 사항으로 돌아가서: 유형 규칙이 너무 비관적이어서 계산 작성의 용이성을 방해하는 경우 우리는 이를 거부할 것입니다. 그러나 표현력을 약간 희생하여 형식 안전성을 달성한다면 우리는 이를 받아들일 것입니다. 예를 들어 반올림 및 강조 기능을 제공하는 개발 환경에서 - 둥근그리고 잘리다, 운영자 n:=r기본 모호한 변환을 사용하는 대신 실수에서 정수로의 변환을 명시적으로 작성해야 하기 때문에 잘못된 것으로 간주됩니다.

정적 타이핑: 방법과 이유

정적 타이핑의 이점은 분명하지만 이에 대해 다시 이야기하는 것이 좋습니다.

장점

우리는 강의 초반에 객체 기술에서 정적 타이핑을 사용하는 이유를 나열했습니다. 이는 신뢰성, 이해 용이성 및 효율성입니다.

신뢰할 수 있음이는 작동 중에만, 일부 경우에만 나타날 수 있는 오류 감지로 인해 발생합니다. 함수뿐만 아니라 엔터티를 선언하도록 하는 첫 번째 규칙은 프로그램 텍스트에 중복성을 도입합니다. 이를 통해 컴파일러는 다른 두 규칙을 사용하여 엔터티, 구성 요소 및 구성 요소의 의도된 사용과 실제 사용 간의 불일치를 감지할 수 있습니다. 표현.

오류를 조기에 발견하는 것도 중요합니다. 오류를 찾는 시간이 길어질수록 수정 비용도 늘어나기 때문입니다. 모든 전문 프로그래머가 직관적으로 이해할 수 있는 이 속성은 Boehm의 잘 알려진 작업을 통해 정량적으로 확인됩니다. 오류를 찾는 시간에 대한 수정 비용의 의존성은 여러 대규모 산업 프로젝트의 데이터와 소규모 제어 프로젝트에서 수행된 실험을 기반으로 한 그래프에 표시됩니다.

쌀. 17.1.비교 버그 수정 비용(허가를 받아 게시됨)

가독성또는 이해하기 쉬운(가독성)에는 장점이 있습니다. 이 책의 모든 예에서 엔터티에 대한 유형의 모양은 독자에게 해당 목적에 대한 정보를 제공합니다. 유지 관리 단계에서는 가독성이 매우 중요합니다.

마지막으로, 능률실제로 사물기술의 성패를 좌우할 수 있다. 실행을 위한 정적 유형 지정이 없는 경우 x.f(인수)시간이 좀 걸릴 수 있습니다. 그 이유는 런타임에 에프기본 대상 클래스에서 엑스, 검색은 해당 하위 항목으로 계속되며 이는 비효율성을 초래하는 올바른 길입니다. 계층 구조에서 구성 요소 검색을 개선하면 문제를 완화할 수 있습니다. Self 언어의 저자는 잘 했어, 동적 유형 언어에 대한 더 나은 코드를 생성하는 것을 목표로 합니다. 그러나 이러한 OO 제품이 효율성 면에서 기존 소프트웨어에 더 가깝거나 동등하게 된 것은 정적 타이핑이었습니다.

정적 타이핑의 핵심은 구문에 대한 코드를 생성하는 컴파일러가 이미 표현한 아이디어입니다. x.f(인수), 유형을 알고 있습니다 엑스. 다형성으로 인해 구성 요소의 적절한 버전을 명확하게 결정하는 것은 불가능합니다. 에프. 그러나 선언은 가능한 유형 세트의 범위를 좁혀 컴파일러가 올바른 유형에 대한 액세스를 제공하는 테이블을 구축할 수 있도록 합니다. 에프최소한의 비용으로 - 제한된 상수로접근의 어려움. 추가 최적화 수행 정적 바인딩그리고 인라인- 또한 정적 타이핑을 통해 더 쉽게 만들어지며 적용되는 오버헤드를 완전히 제거합니다.

동적 타이핑의 경우

이 모든 것에도 불구하고 동적 타이핑은 특히 Smalltalk 프로그래머 사이에서 지지자를 잃지 않습니다. 그들의 주장은 주로 위에서 논의한 현실주의에 기초하고 있습니다. 그들은 정적 타이핑이 너무 제한적이어서 창의적인 아이디어를 자유롭게 표현하는 것을 방해하고 때로는 이를 "순결 벨트"라고 부르기도 한다고 믿습니다.

이러한 추론에 동의할 수 있지만 여러 기능을 지원하지 않는 정적으로 유형이 지정된 언어에만 해당됩니다. 유형 개념과 관련되고 이전 강의에서 소개된 모든 개념이 필요하다는 점은 주목할 가치가 있습니다. 그 중 하나를 거부하면 심각한 제한이 따르며 반대로 도입하면 행동 유연성이 제공되고 정적 타이핑의 실용성을 마음껏 누릴 수 있는 기회.

타이핑: 성공의 구성요소

사실적인 정적 타이핑을 위한 메커니즘은 무엇입니까? 모두 이전 강의에서 소개되었기 때문에 간략하게만 기억할 수 있을 뿐입니다. 이들을 함께 나열하면 연관성의 일관성과 힘이 드러납니다.

우리의 유형 시스템은 전적으로 다음 개념을 기반으로 합니다. 수업. 그런 기본적인 유형조차도 정수, 따라서 미리 정의된 유형을 설명하는 데 특별한 규칙이 필요하지 않습니다. (이것이 우리의 표기법이 이전 언어의 유형 시스템과 클래스 기반 개체 기술을 결합한 Object Pascal, Java 및 C++와 같은 "하이브리드" 언어와 다른 점입니다.)

확장형값이 객체를 나타내는 유형과 값이 참조를 나타내는 유형을 허용함으로써 더 많은 유연성을 제공합니다.

유연한 유형 시스템을 만드는 데 결정적인 단어는 다음과 같습니다. 계승그리고 이와 관련된 개념 호환성. 이는 Pascal 및 Ada와 같은 고전적인 유형 언어의 주요 한계를 극복합니다. x:=y유형이 필요합니다. 엑스그리고 와이동일했습니다. 이 규칙은 너무 엄격합니다. 관련 유형의 객체를 나타낼 수 있는 엔터티의 사용을 금지합니다( 예금 계좌그리고 계정 확인). 상속할 때 유형 호환성만 필요합니다. 와이유형으로 엑스, 예를 들어, 엑스유형이 있음 계정, y- 예금 계좌, 두 번째 클래스는 첫 번째 클래스의 상속인입니다.

실제로 정적으로 유형이 지정된 언어에는 지원이 필요합니다. 다중 상속. 정적 타이핑을 사용하면 객체가 다르게 해석되는 것을 허용하지 않는다는 근본적인 비난이 잘 알려져 있습니다. 응, 그 물체 문서(문서)는 네트워크를 통해 전송될 수 있으므로 유형과 관련된 구성 요소가 필요합니다. 메시지(메시지). 그러나 이러한 비판은 단일 상속으로 제한된 언어에만 적용됩니다.

쌀. 17.2.다중 상속

다재예를 들어 유연하지만 안전한 컨테이너 데이터 구조를 설명하는 데 필요합니다(예: 클래스 목록 [G] ...). 이 메커니즘이 없으면 정적 유형 지정을 위해서는 요소 유형이 다른 목록에 대해 서로 다른 클래스를 선언해야 합니다.

경우에 따라 다용도가 필요함 한계, 일반 유형의 엔터티에만 적용되는 작업을 사용할 수 있습니다. 일반 클래스인 경우 정렬 가능_LIST정렬을 지원하며 유형의 엔터티가 필요합니다. G, 어디 G- 일반 매개변수, 비교 연산의 존재. 이는 다음과 연결하여 달성됩니다. G일반적인 제한을 지정하는 클래스 - 유사한:


클래스 SORTABLE_LIST ...

실제 일반 매개변수 정렬 가능_LIST클래스의 자손이어야 합니다. 유사한, 필수 구성 요소가 있습니다.

또 다른 필수 메커니즘은 할당 시도- 소프트웨어가 제어하지 않는 유형의 개체에 대한 액세스를 구성합니다. 만약에 와이데이터베이스 개체이거나 네트워크를 통해 수신된 개체인 경우 운영자는 x ?= y적절할 것이다 엑스의미 와이, 만약에 와이호환 가능한 유형이거나, 그렇지 않은 경우 다음을 제공합니다. 엑스의미 무효의.

진술계약에 의한 설계 아이디어의 일부로서, 전제 조건, 사후 조건 및 클래스 불변의 형태로 클래스 및 해당 구성요소와 연관되어 유형 사양에서 다루지 않는 의미론적 제약을 설명하는 것이 가능해집니다. Pascal 및 Ada와 같은 언어에는 엔터티 값을 가령 10~20으로 제한할 수 있는 범위 유형이 있지만 값을 보장하기 위해 이를 사용할 수는 없습니다. 부정적이었고 항상 두 배였습니다. 제이. 클래스 불변성은 아무리 복잡하더라도 부과된 제한 사항을 정확하게 반영하도록 설계되었습니다.

고정된 광고실제로 코드 중복이 발생하는 것을 방지하려면 필요합니다. 발표 y: x처럼, 당신은 다음과 같은 보장을 받습니다 와이해당 유형의 반복 선언에 따라 변경됩니다. 엑스자손에게서. 이 메커니즘이 없으면 개발자는 다양한 유형 간의 일관성을 유지하기 위해 지속적으로 다시 선언하게 됩니다.

고정 선언은 우리가 요구하는 마지막 언어 메커니즘의 특별한 경우입니다. 공분산, 이에 대해서는 나중에 자세히 논의하겠습니다.

개발 중 소프트웨어 시스템실제로 개발 환경 자체에 내재된 속성이 하나 더 필요합니다. 빠른 증분 재컴파일. 시스템을 작성하거나 수정할 때 변경 사항의 효과를 최대한 빨리 확인하고 싶을 것입니다. 정적 유형 지정을 사용하면 컴파일러에 유형을 다시 확인할 시간을 주어야 합니다. 기존의 컴파일 루틴에서는 전체 시스템을 다시 번역해야 합니다(그리고 그 어셈블리), 이 프로세스는 특히 더 큰 규모의 시스템으로 이동할 때 매우 길어질 수 있습니다. 이 현상은 찬성론이 되었다 해석적초기 Lisp 또는 Smalltalk 환경과 같은 시스템은 처리나 유형 확인이 거의 또는 전혀 없이 시스템을 실행했습니다. 이 주장은 이제 잊혀졌습니다. 좋은 최신 컴파일러는 마지막 컴파일 이후 코드가 어떻게 변경되었는지 확인하고 발견된 변경 사항만 처리합니다.

“아기가 입력되었나요?”

우리의 목표 - 엄격한정적 타이핑. 이것이 바로 우리가 "규칙에 따른 게임"에서 허점을 피해야 하는 이유입니다. 적어도 허점이 존재한다면 이를 정확하게 식별해야 합니다.

정적으로 유형이 지정된 언어에서 가장 일반적인 허점은 엔터티 유형을 변경하는 변환이 있다는 것입니다. C 및 그 파생 언어에서는 이를 "유형 캐스팅" 또는 캐스팅이라고 합니다. 기록 (OTHER_TYPE)x값을 나타냅니다. 엑스컴파일러는 유형을 갖는 것으로 인식합니다. OTHER_TYPE, 가능한 유형에 대한 특정 제한이 적용됩니다.

이러한 메커니즘은 유형 검사의 한계를 우회합니다. 캐스팅은 ANSI C 방언을 포함한 C 프로그래밍에서 일반적입니다. C++에서도 유형 캐스팅은 흔하지는 않지만 여전히 일반적이고 필요할 수 있습니다.

캐스팅을 통해 언제든지 우회할 수 있다면 정적 타이핑 규칙을 고수하는 것은 그리 쉽지 않습니다.

입력 및 연결

이 책의 독자라면 아마도 정적 타이핑과 정적 타이핑을 구별할 것입니다. 제본, 이것을 할 수없는 사람들이 있습니다. 이는 부분적으로 두 문제에 대한 역동적인 접근 방식을 옹호하고 두 문제가 동일한 해결책을 가지고 있다는 오해로 이어질 수 있는 스몰토크의 영향 때문일 수 있습니다. (저희 책에서는 안정적이고 유연한 프로그램을 만들려면 정적 타이핑과 동적 바인딩을 결합하는 것이 바람직하다고 주장합니다.)

타이핑과 바인딩 모두 기본 구성의 의미를 다룹니다. x.f(인수), 그러나 두 가지 다른 질문에 대답하십시오.

입력 및 연결

[엑스]. 질문을 입력하는 중: 다음에 해당하는 작업이 있는지 확실히 알아야 할 때 에프, 엔터티에 연결된 개체에 적용 가능 엑스(매개변수 포함 인수)?

[엑스]. 연결 질문: 주어진 호출이 어떤 작업을 시작하는지 언제 알아야 합니까?

입력하면 가용성 문제에 대한 답변이 제공됩니다. 적어도 하나작업, 바인딩은 선택을 담당합니다. 필요한.

객체 접근 내에서:

[엑스].타이핑에서 발생하는 문제는 다음과 관련이 있습니다. 다형성: 왜냐하면 엑스실행 중여러 가지 다른 유형의 객체를 나타낼 수 있으므로 다음을 나타내는 연산이 있는지 확인해야 합니다. 에프, 사용 가능이 각각의 경우에;

[엑스].바인딩 문제 발생 반복되는 발표: 클래스는 상속된 구성 요소를 변경할 수 있으므로 다음을 나타내는 작업이 두 개 이상 있을 수 있습니다. 에프이 통화에서.

두 문제 모두 동적으로나 정적으로 해결될 수 있습니다. 기존 언어는 4가지 솔루션을 모두 제공합니다.

[엑스]. Pascal과 Ada와 같은 다수의 비객체 언어는 정적 타이핑과 정적 바인딩을 모두 구현합니다. 각 엔터티는 정적으로 정의된 한 가지 유형의 개체만 나타냅니다. 이는 솔루션의 신뢰성을 보장하며 그 대가는 유연성입니다.

[엑스].스몰토크 및 기타 객체 지향 언어에는 동적 바인딩 및 동적 타이핑 기능이 포함되어 있습니다. 이 경우 언어의 신뢰성을 희생하면서 유연성을 선호합니다.

[엑스].일부 비객체 언어는 동적 타이핑과 정적 링크를 지원합니다. 그 중에는 어셈블리 언어와 다양한 스크립팅 언어가 있습니다.

[엑스].정적 타이핑과 동적 바인딩의 개념은 이 책에서 제안하는 표기법에 구현되어 있습니다.

유형 캐스팅, 정적 바인딩(기본값), 명시적인 가상 표시가 있는 동적 바인딩(기본값)으로 인해 엄격하지는 않지만 정적 유형 지정을 지원하는 C++ 언어의 독창성에 주목해 보겠습니다. 가상) 광고.

정적 타이핑과 동적 바인딩을 선택하는 이유는 분명합니다. 첫 번째 질문은 "구성 요소의 존재를 언제 알 수 있습니까?"입니다. - 정적인 답변을 제안합니다: " 빠를수록 좋다", 즉 컴파일 타임을 의미합니다. 두 번째 질문: "어떤 구성 요소를 사용해야 합니까?"에는 동적 답변이 필요합니다. " 당신이 필요로하는 것", - 런타임 시 결정된 객체의 동적 유형에 해당합니다. 이는 정적 바인딩과 동적 바인딩이 서로 다른 결과를 생성하는 경우 허용되는 유일한 솔루션입니다.

상속 계층 구조의 다음 예는 이러한 개념을 명확하게 하는 데 도움이 됩니다.

쌀. 17.3.항공기 종류

다음과 같은 과제를 고려해보세요.


my_aircraft.lower_landing_gear

질문 입력: 여기에 구성요소가 있는지 언제 확인해야 하나요? lower_landing_gear("하부 랜딩 기어"), 물체에 적용 가능(용 헬리콥터전혀 존재하지 않습니다) 바인딩에 대한 질문: 여러 가지 가능한 버전 중 어떤 버전을 선택할지.

정적 바인딩은 연결되는 객체의 유형을 무시하고 엔터티 선언에 의존한다는 것을 의미합니다. 결과적으로, 보잉 747-400을 다룰 때 우리는 747-400 개조용이 아닌 일반 747 시리즈 여객기용으로 설계된 버전을 요구하게 되었습니다. 동적 바인딩은 개체에 필요한 작업을 적용하며 이것이 올바른 접근 방식입니다.

정적 타이핑을 사용하면 프로그램이 엔터티에 대해 실행될 때 컴파일러가 호출을 거부하지 않습니다. my_aircraft해당 구성요소와 함께 제공된 객체 lower_landing_gear. 보증을 얻는 기본 기술은 간단합니다. 의무적 발표 시 my_aircraft그러한 구성 요소를 포함하려면 해당 유형의 기본 클래스가 필요합니다. 그렇기 때문에 my_aircraft다음과 같이 선언할 수 없습니다. 항공기, 후자는 없기 때문에 lower_landing_gear이 수준에서는; 적어도 우리의 예에서 헬리콥터는 랜딩 기어를 내리는 방법을 모릅니다. 엔터티를 다음과 같이 선언하면 비행기, - 필수 구성 요소가 포함된 클래스 - 모든 것이 정상입니다.

Smalltalk 스타일의 동적 타이핑을 위해서는 호출을 기다려야 하며 실행 시 원하는 구성 요소가 있는지 확인해야 합니다. 이 동작은 프로토타입 및 실험 개발에서는 가능하지만 산업용 시스템에서는 허용되지 않습니다. 비행 중에 랜딩 기어가 있는지 물어보기에는 너무 늦습니다.

공분산과 자식 숨기기

세상이 단순하다면 타이핑에 대한 대화는 끝날 수도 있습니다. 우리는 정적 타이핑의 목표와 이점을 확인하고, 현실적인 타입 시스템이 충족해야 하는 제약 조건을 조사했으며, 제안된 타이핑 방법이 우리의 기준을 충족하는지 확인했습니다.

하지만 세상은 단순하지 않습니다. 정적 타이핑과 일부 소프트웨어 엔지니어링 요구 사항을 결합하면 눈에 보이는 것보다 더 복잡한 문제가 발생합니다. 두 가지 메커니즘으로 인해 문제가 발생합니다. 공분산- 재정의 시 매개변수 유형 변경, 자손 숨기기- 상속된 구성 요소의 내보내기 상태를 제한하는 하위 클래스의 기능입니다.

공분산

해당 유형이 재정의되면 구성 요소의 인수는 어떻게 되나요? 이것은 중요한 문제이며 우리는 이미 장치와 프린터, 단일 및 이중 연결 목록 등 그 징후의 여러 예를 보았습니다(섹션 16.6, 16.7 참조).

문제의 본질을 명확히 하는 데 도움이 되는 또 다른 예는 다음과 같습니다. 그리고 비록 현실과 거리가 멀고 은유적이긴 하지만, 소프트웨어 체계에 가깝다는 것은 분명합니다. 또한 분석할 때 실제 문제로 돌아가는 경우가 많습니다.

챔피언십을 준비하는 대학 스키팀을 상상해 봅시다. 수업 소녀여자팀에 출전하는 스키 선수도 포함되며, 소년- 스키어. 양 팀의 참가자 중 상당수는 이전 대회에서 좋은 성적을 거뒀던 만큼 순위가 매겨졌다. 이제 그들은 먼저 실행하여 다른 사람들보다 이점을 얻을 것이기 때문에 이것은 그들에게 중요합니다. (이미 특권을 받은 사람들에게 특권을 부여하는 이 규칙은 아마도 많은 사람들의 눈에 회전과 크로스컨트리 스키를 그토록 매력적으로 만드는 이유일 것이며, 삶 자체에 대한 좋은 비유가 될 것입니다.) 그래서 우리는 두 가지 새로운 클래스를 갖게 되었습니다. RANKED_GIRL그리고 RANKED_BOY.

쌀. 17.4.스키어 분류

운동선수들이 머물 수 있도록 많은 방이 예약되어 있습니다. 남성 전용, 소녀 전용, 수상 경력이 있는 소녀 전용입니다. 이를 표시하기 위해 병렬 클래스 계층 구조를 사용합니다. , GIRL_ROOM그리고 RANKED_GIRL_ROOM.

수업 개요는 다음과 같습니다. 스키 타는 사람:


- 룸메이트.
...이 클래스와 후속 클래스에서는 생략된 다른 가능한 구성요소...

우리는 두 가지 구성 요소에 관심이 있습니다. 룸메이트및 절차 공유하다, 현재 스키어와 같은 방에 이 스키어를 "배치"합니다.


엔터티를 선언할 때 다른유형을 거부할 수 있습니다. 스키 타는 사람고정형을 선호한다 룸메이트처럼(또는 현재처럼을 위한 룸메이트그리고 다른동시에). 그러나 유형 할당에 대해서는 잠시 잊어버리고(나중에 다시 설명하겠습니다) 원래 형태의 공분산 문제를 살펴보겠습니다.

유형 재정의를 도입하는 방법은 무엇입니까? 규칙에 따르면 남학생과 여학생, 우승자 및 기타 참가자를 위한 별도의 숙소가 필요합니다. 이 문제를 해결하기 위해 재정의할 때 구성 요소의 유형을 변경해 보겠습니다. 룸메이트, 아래와 같이(여기와 아래에서 재정의된 요소에 밑줄이 그어져 있음)


- 룸메이트.

이에 따라 프로시저 인수를 재정의해 보겠습니다. 공유하다. 이제 클래스의 더 완전한 버전은 다음과 같습니다:


- 룸메이트.
-- 다른 사람을 이웃으로 선택합니다.

마찬가지로, 다음에서 생성된 모든 항목을 변경해야 합니다. 스키 타는 사람클래스(현재 유형 바인딩을 사용하지 않습니다). 결과적으로 다음과 같은 계층 구조가 있습니다.

쌀. 17.5.참가자 계층 구조 및 재정의

상속은 특수화이므로 유형 규칙에서는 구성 요소의 결과를 재정의할 때 다음을 요구합니다. 룸메이트, 새로운 유형은 원래 유형의 후손이었습니다. 인수 유형을 재정의하는 경우에도 동일하게 적용됩니다. 다른서브루틴 공유하다. 우리가 알고 있듯이 이 전략을 공분산이라고 하며, 여기서 접두사 "co"는 매개변수 유형과 결과의 공동 변경을 나타냅니다. 반대 전략이라고 합니다. 반변성.

우리의 모든 예는 공분산이 실제로 필요하다는 설득력 있는 증거를 제공합니다.

[엑스].단일 연결 리스트 요소 연결 가능다른 유사한 요소와 연결되어야 하며 인스턴스는 BI_LINKABLE- 자기 같은 사람이랑. 공변적으로 재정의되어야 하며 똑바로 넣어.

[엑스].모든 서브루틴 LINKED_LIST다음과 같은 주장으로 연결 가능이사할 때 TWO_WAY_LIST논쟁이 필요할 것이다 BI_LINKABLE.

[엑스].절차 set_alternate받아들인다 장치-수업 중 논쟁 장치그리고 인쇄기-인수 - 수업 중 인쇄기.

정보 은폐로 인해 다음 형식의 프로시저가 생성되기 때문에 공변적 재정의가 특히 널리 퍼졌습니다.


-- 속성을 v로 설정합니다.

함께 일하다 속성유형 SOME_TYPE. 속성의 유형을 변경하는 모든 클래스는 이에 따라 인수를 재정의해야 하기 때문에 이러한 절차는 당연히 공변적입니다. set_attrib. 제시된 예는 하나의 체계에 적합하지만 공분산은 훨씬 더 널리 퍼져 있습니다. 예를 들어, 단일 연결 목록의 연결을 수행하는 프로시저나 함수를 생각해 보세요( LINKED_LIST). 해당 인수는 이중 연결 목록( TWO_WAY_LIST). 범용 추가 작업 중위 "+"받아들인다 숫자-수업 중 논쟁 숫자, 진짜- 클래스 진짜그리고 정수- 클래스 정수. 절차에 대한 병렬 전화 서비스 계층 구조 시작클래스 PHONE_SERVICE인수가 필요할 수 있음 주소, 가입자의 주소를 나타냄(청구용), 클래스에서도 동일한 절차 CORPORATE_SERVICE다음과 같은 인수가 필요합니다 CORPORATE_ADDRESS.

쌀. 17.6.통신 서비스

반공변 솔루션은 어떻습니까? 스키어의 예에서 이는 다음을 의미합니다. RANKED_GIRL, 결과 유형 룸메이트다음과 같이 재정의 RANKED_GIRL, 그러면 반공변성(contravariance)으로 인해 인수 유형이 공유하다유형을 재정의할 수 있습니다. 소녀또는 스키 타는 사람. 반공변 솔루션에서 허용되지 않는 유일한 유형은 다음과 같습니다. RANKED_GIRL! 소녀들의 부모들 사이에서 최악의 의심을 불러일으키기에 충분했습니다.

병렬 계층

모든 문제를 해결하기 위해 예제의 변형을 고려해 보겠습니다. 스키 타는 사람두 개의 병렬 계층 구조가 있습니다. 이를 통해 실제로 이미 발생한 상황을 시뮬레이션할 수 있습니다. TWO_WAY_LIST > LINKED_LIST그리고 BI_LINKABLE > 링크 가능; 또는 전화 서비스를 통한 계층 구조 PHONE_SERVICE.

클래스에 계층 구조를 두세요 , 그 후손은 GIRL_ROOM(수업 소년생략):

쌀. 17.7.스키어와 객실

대신 이 병렬 계층 구조의 스키어 클래스 룸메이트그리고 공유하다비슷한 구성 요소가 있을 것입니다. 숙소 (숙소) 그리고 수용하다 (우편):


설명: "병렬 계층 구조를 갖춘 새로운 옵션"
수용 (r: ROOM)은 ... 요구합니다 ... 수행합니다

여기에서도 공변적 재정의가 필요합니다: 클래스에서 여자1어떻게 숙소및 서브루틴 인수 수용하다유형으로 대체해야 합니다. GIRL_ROOM, 클래스 소년1- 유형 BOY_ROOM등. (우리는 여전히 유형 앵커링 없이 작업하고 있다는 점을 기억하십시오.) 이전 예와 마찬가지로 여기서는 반공변성이 유용하지 않습니다.

다형성의 변덕스러움

공분산의 실용성을 뒷받침하는 예가 충분하지 않습니까? 왜 실제로 필요한 것과 충돌하는 반공변성(일부 젊은이들의 행동은 제외)을 고려하겠습니까? 이를 이해하려면 다형성과 공분산 전략을 결합할 때 발생하는 문제를 고려하십시오. 방해 행위 계획을 세우는 것은 어렵지 않으며 이미 스스로 계획을 세웠을 수도 있습니다.


b를 생성하고; create g;-- BOY 및 GIRL 객체를 생성합니다.

마지막 호출의 결과는 소년들에게 매우 만족스러울 수도 있지만 유형 재정의를 통해 우리가 방지하려고 했던 것과 정확히 같습니다. 부르다 공유하다그 물건이 그 물건이라는 사실로 이어진다. 소년, 로 알려진 다형성 덕분에 가명을 얻었습니다. 에스유형 스키 타는 사람, 객체의 이웃이 됩니다 소녀, 로 알려진 g. 그러나 이 도전은 호스텔 규칙에 어긋나지만 프로그램 텍스트에서는 매우 정확합니다. 공유하다-컴포지션의 내보낸 구성 요소 스키 타는 사람, ㅏ 소녀, 인수 유형 g, 호환 가능 스키 타는 사람, 형식 매개변수 유형 공유하다.

병렬 계층 구조도 마찬가지로 간단합니다. 스키 타는 사람~에 스키어1, 도전 공유하다- 통화 s.accommodate(gr), 어디 gr- 엔터티 유형 GIRL_ROOM. 결과는 동일합니다.

반공변 솔루션을 사용하면 다음과 같은 문제가 발생하지 않습니다. 호출 대상의 특수화(이 예에서는 에스) 논증의 일반화가 필요합니다. 반변성(Contravariance)은 메커니즘의 더 간단한 수학적 모델(상속 - 재정의 - 다형성)을 생성합니다. 이 사실은 이 전략을 제안하는 수많은 이론적 논문에 설명되어 있습니다. 우리의 사례와 다른 출판물에서 알 수 있듯이 반변성은 다음을 갖지 않기 때문에 이 주장은 그다지 설득력이 없습니다. 실제 사용.

그러므로 공변적 신체에 반변적 옷을 입히려고 하지 말고, 공변적 현실을 받아들이고 이를 제거하는 방법을 모색해야 한다. 바람직하지 않은 효과.

아이에게 숨어서

공분산 문제에 대한 해결책을 찾기 전에 다형성 조건에서 유형 위반을 일으킬 수 있는 또 다른 메커니즘을 고려해 보겠습니다. 하위 항목 숨기기는 상위 항목에서 파생된 구성 요소를 내보내지 않는 클래스의 기능입니다.

쌀. 17.8.아이에게 숨어서

전형적인 예는 구성 요소입니다. add_vertex(정점 추가) 클래스별로 내보냄 다각형, 그러나 그 하위 항목에 의해 숨겨짐 직사각형(불변성 위반 가능성으로 인해 클래스는 직사각형을 유지하기를 원합니다):


프로그래머가 아닌 예: "Ostrich" 클래스는 부모 "Bird"로부터 받은 "Fly" 메서드를 숨깁니다.

잠시 이 계획을 그대로 받아들여 상속과 숨김의 조합이 합법적인지 물어보겠습니다. 공분산과 마찬가지로 숨김의 모델링 역할은 다형성으로 인해 가능한 트릭에 의해 위반됩니다. 그리고 여기에서는 구성 요소를 숨겼음에도 불구하고 구성 요소를 호출하고 사각형에 꼭지점을 추가할 수 있는 악의적인 예제를 구성하는 것이 어렵지 않습니다.


r을 생성하고; -- RECTANGLE 객체를 생성합니다.
p:=r; -- 다형성 할당.

개체 이후로 아르 자형본질 아래 숨어 있다 수업 다각형, ㅏ add_vertex내보낸 구성요소 다각형, 본질적으로 그 도전 옳은. 실행 결과 직사각형에 또 다른 정점이 나타나며 이는 잘못된 객체가 생성됨을 의미합니다.

시스템 및 클래스의 정확성

공분산과 자식 숨기기 문제를 논의하려면 몇 가지 새로운 용어가 필요합니다. 우리가 전화할게 클래스 유효강의 시작 부분에 제시된 세 가지 유형 설명 규칙을 만족하는 시스템입니다. 각 엔터티에는 고유한 유형이 있다는 점을 상기시켜 드리겠습니다. 실제 인수의 유형은 할당과 유사한 상황인 형식 인수의 유형과 호환되어야 합니다. 호출될 Bean은 자체 클래스에서 선언되어야 하며 호출을 포함하는 클래스로 내보내야 합니다.

시스템이 호출됩니다. 시스템 유효, 실행 중에 유형 위반이 발생하지 않는 경우.

이상적으로는 두 개념이 일치해야 합니다. 그러나 우리는 상속, 공분산 및 자손에 의한 숨김 조건에서 클래스가 올바른 시스템이 시스템이 올바르지 않을 수 있다는 것을 이미 살펴보았습니다. 이것을 오류라고 부르자 시스템 유효성 오류 위반.

실용적인 측면

문제의 단순성은 일종의 역설을 만듭니다. 호기심이 많은 초보자는 몇 분 안에 반례를 만들 것입니다. 실제로 시스템의 클래스 정확성 오류는 매일 발생하지만 시스템 정확성 위반은 대규모 다중 환경에서도 발생합니다. 연간 프로젝트는 극히 드물게 발생합니다.

그러나 이것이 우리가 이를 무시하는 것을 허용하지 않으므로 이 문제를 해결하기 위한 세 가지 가능한 방법을 연구하기 시작합니다.

다음으로, 객체 접근 방식의 매우 미묘하고 자주 드러나지 않는 측면을 다룰 것입니다. 이 책을 처음 읽는 경우에는 이 강의의 나머지 부분을 건너뛰어도 됩니다. 최근에 OO 기술을 접하신 분이라면 상속 방법론을 다룬 "객체 지향 설계의 기초" 강좌 1~11강, 특히 "객체 지향 설계의 기초" 강좌 6강을 공부하신 후 이 자료를 더 잘 이해하실 수 있을 것입니다. Oriented Design' 과정은 방법론 계승에 전념합니다.

시스템의 정확성: 첫 번째 근사치

먼저 고려된 두 가지 문제 중 더 중요한 공분산 문제에 집중해 보겠습니다. 이 주제에 관한 광범위한 문헌이 있으며 다양한 솔루션을 제공합니다.

반변성과 비변성

반공변성은 시스템 정확성 위반과 관련된 이론적 문제를 제거합니다. 그러나 이는 유형 시스템의 현실성을 잃기 때문에 이 접근 방식을 더 이상 고려할 필요가 없습니다.

C++ 언어의 독창성은 전략을 사용한다는 것입니다. 불변성, 재정의된 루틴에서 인수 유형을 변경할 수 없습니다! C++가 강력한 형식의 언어라면 시스템 유형사용하기 어려울 것입니다. 이 언어의 문제에 대한 가장 간단한 해결책은 C++의 다른 제한 사항(예: 제한된 보편성 부족)을 우회하는 것뿐만 아니라 기존 타이핑 메커니즘을 완전히 무시할 수 있는 캐스팅 - 유형 캐스팅을 사용하는 것입니다. 이 솔루션은 매력적이지 않은 것 같습니다. 그러나 아래에서 논의되는 많은 제안은 비변성에 의존할 것이며, 그 의미는 공변성 재정의 대신 유형 작업을 위한 새로운 메커니즘의 도입으로 제공될 것입니다.

일반 매개변수 사용

보편성은 프란츠 베버(Franz Weber)가 처음 표현한 흥미로운 아이디어의 핵심입니다. 클래스를 선언해보자 스키어1, 일반 매개변수의 보편화를 클래스로 제한 :


클래스 SKIER1 기능
수용하다 (r: G)는 ... 필요하다 ... 수용을 한다:= r end

그럼 수업 여자1상속자가 될 것이다 스키어1등등. 언뜻 보기에는 아무리 이상해 보일지라도 동일한 기술을 병렬 계층 구조가 없는 경우에도 사용할 수 있습니다. 클래스 스키어.

이 접근 방식은 공분산 문제를 해결합니다. 클래스를 사용하려면 실제 일반 매개변수를 지정해야 합니다. 또는 GIRL_ROOM, 따라서 잘못된 조합이 불가능해집니다. 언어는 변형이 없으며 시스템은 일반 매개변수 덕분에 공분산 요구 사항을 완전히 충족합니다.

불행하게도 이 기술은 가능한 공변 인수의 각 유형에 대해 하나씩 일반 매개변수 목록이 늘어나기 때문에 일반적인 솔루션으로는 적합하지 않습니다. 아직 더 나쁨, 목록에 없는 유형의 인수를 사용하여 공변 서브루틴을 추가하면 일반 클래스 매개변수를 추가해야 하므로 클래스 인터페이스가 변경되어 클래스의 모든 클라이언트에 대한 변경이 발생하며 이는 허용되지 않습니다.

일반적인 변수

Kim Bruce, David Shang, Tony Simons를 비롯한 많은 저자들이 값이 유형인 유형 변수를 기반으로 한 솔루션을 제안했습니다. 그들의 아이디어는 간단합니다.

[엑스].공변 재정의 대신 유형 변수를 사용하는 유형 선언을 허용합니다.

[엑스].이러한 변수를 관리하기 위해 유형 호환성 규칙을 확장합니다.

[엑스].언어 유형을 유형 변수에 대한 값으로 할당하는 기능을 제공합니다.

독자들은 이 주제에 관한 여러 기사와 Cardelli, Castagna, Weber 등의 출판물에서 이러한 아이디어에 대한 자세한 프레젠테이션을 찾을 수 있습니다. 이 강의의 참고문헌에 표시된 출처에서 문제 연구를 시작할 수 있습니다. . 우리는 이 문제를 다루지 않을 것이며 그 이유는 다음과 같습니다.

[엑스].적절하게 구현된 유형 변수 메커니즘은 유형을 완전히 지정하지 않고도 유형을 사용할 수 있도록 허용하는 범주에 속합니다. 동일한 카테고리에는 다양성과 광고 고정이 포함됩니다. 이 메커니즘은 이 범주의 다른 메커니즘을 대체할 수 있습니다. 이는 처음에는 유형 변수를 선호하는 것으로 해석될 수 있지만, 이 모든 것을 포괄하는 메커니즘이 보편성과 유형 고정에 내재된 쉽고 단순하게 모든 작업을 처리할 수 있는지가 확실하지 않기 때문에 결과는 비참할 수 있습니다.

[엑스].공분산과 다형성을 결합하는 문제(아직 자식 숨기기 문제는 무시함)를 극복할 수 있는 유형 변수 메커니즘이 개발되었다고 가정해 보겠습니다. 그러면 클래스 개발자가 필요합니다. 특별한 직관생성된 클래스의 유형을 재정의하는 데 사용할 수 있는 구성 요소와 그렇지 않은 구성 요소를 미리 결정하기 위해. 아래에서는 프로그램 작성 실습에서 발생하는 이 문제에 대해 논의하고 아쉽게도 많은 이론적 체계의 적용 가능성에 의문을 제기합니다.

이로 인해 우리는 이미 논의된 메커니즘, 즉 제한적이고 무제한적인 보편성, 유형 고정 및 상속으로 돌아가게 됩니다.

유형 앵커링에 의존

우리가 알고 있는 고정광고의 메커니즘을 자세히 살펴보면 공분산 문제에 대한 거의 기성화된 해결책을 찾을 수 있을 것입니다.

클래스를 설명할 때 스키 타는 사람그리고 스키어1많은 재정의를 제거하기 위해 고정된 광고를 사용하고 싶은 유혹을 느낄 수밖에 없습니다. 앵커링은 일반적인 공변 메커니즘입니다. 다음은 예시의 모습입니다(모든 변경 사항에 밑줄이 그어져 있습니다).


공유(기타: 현재와 유사)는 ... 요구 ... 수행
수용하다 (r: 숙소와 같다)는 ... 필요하다 ... 하다

이제 후손이 클래스를 떠날 수 있습니다. 스키 타는 사람변화는 없지만 스키어1속성만 재정의하면 됩니다. 숙소. 고정된 엔터티: 속성 룸메이트및 서브루틴 인수 공유하다그리고 수용하다- 자동으로 변경됩니다. 이는 작업을 크게 단순화하고 고정(또는 유형 변수와 같은 다른 유사한 메커니즘)이 없으면 사실적인 타이핑으로 객체 지향 소프트웨어 제품을 작성하는 것이 불가능하다는 사실을 강화합니다.

그러나 시스템의 정확성에 대한 위반을 제거하는 것이 가능했습니까? 아니요! 시스템 정확성 위반을 유발하는 다형성 할당을 수행하면 여전히 유형 검사를 능가할 수 있습니다.

그러나 예제의 원본 버전은 거부됩니다. 하자:


create b;create g;-- BOY 및 GIRL 객체를 생성합니다.
s:= b; -- 다형성 할당.

논쟁 g, 전송 공유하다는 유형의 객체가 필요하기 때문에 이제 올바르지 않습니다. 처럼, 그리고 수업 소녀앵커 유형 규칙에 따라 호환되는 유형이 없기 때문에 이 유형과 호환되지 않습니다. 처럼그 자신 외에는.

그러나 우리는 오랫동안 행복하지 않을 것입니다. 반면에 이 규칙은 다음과 같이 말합니다. 처럼유형과 호환 가능 에스. 이는 객체의 다형성뿐만 아니라 다형성을 사용하는 것을 의미합니다. 에스, 또한 매개변수 g, 유형 검사 시스템을 다시 우회할 수 있습니다.


들: 스키어; 비보이; g: s처럼; 실제_g: 소녀;
b를 생성하고; 실제_g 생성 - BOY 및 GIRL 객체를 생성합니다.
s:= 실제_g; g:= s -- s를 사용하여 g를 GIRL에 연결합니다.
s:= b -- 다형성 할당.

결과적으로 불법 통화가 이루어지게 됩니다.

탈출구가 있습니다. 선언 고정을 유일한 공분산 메커니즘으로 사용하는 것을 진지하게 고려한다면 고정된 엔터티의 다형성을 완전히 금지하여 시스템 정확성 위반을 제거할 수 있습니다. 이를 위해서는 언어 변경이 필요합니다. 새 키워드를 소개하겠습니다. (이 논의에만 사용하기 위해 이 가상 구성이 필요합니다):


형식의 선언을 허용해 보겠습니다. 처럼일 때만 에스~로 서술 된 . 우리는 다음을 보장하기 위해 호환성 규칙을 변경할 것입니다. 에스그리고 같은 요소 처럼(할당이나 인수 전달 시) 서로만 연결할 수 있습니다.

이 접근 방식을 사용하면 서브루틴 인수의 유형을 재정의하는 기능을 언어에서 제거합니다. 또한 결과 유형이 재정의되는 것을 방지할 수 있지만 반드시 그럴 필요는 없습니다. 물론 속성 유형을 재정의하는 기능은 그대로 유지됩니다. 모두인수 유형 재정의는 이제 공분산에 의해 트리거되는 고정 메커니즘을 통해 암시적으로 수행됩니다. 이전 접근 방식을 사용하면 클래스가 상속된 구성요소를 다음과 같이 재정의했습니다.


반면 수업은 - 부모의 보였습니다


어디 와이해당 엑스, 이제 구성 요소 재정의 아르 자형다음과 같이 보일 것입니다 :


교실에만 남는다 재정의 유형 your_anchor.

우리는 공분산 문제에 대한 이 해법을 다형성이라고 부를 것입니다. 앵커링. "공변은 통합을 통해서만 가능하다"라고 말하는 것이 더 정확할 것입니다. 접근 방식의 특성은 매력적입니다.

[엑스].강화는 엄격한 분리 사상을 기반으로합니다. 공변그리고 잠재적으로 다형성(또는 줄여서 다형성) 요소입니다. 다음으로 선언된 모든 엔터티 또는 some_anchor처럼공변량; 다른 것들은 다형성입니다. 두 범주 각각에는 어떠한 부가도 허용되나, 경계를 위반하는 개체나 표현은 없습니다. 예를 들어 다형성 소스를 공변 대상에 할당할 수 없습니다.

[엑스].이 간단하고 우아한 솔루션은 초보자도 쉽게 설명할 수 있습니다.

[엑스].이는 공변적으로 구성된 시스템에서 시스템 정확성을 위반할 가능성을 완전히 제거합니다.

[엑스].이는 제한적 및 무제한적 보편성의 개념을 포함하여 위에서 설명한 개념적 기반을 유지합니다. (결과적으로 이 솔루션은 다양한 실제 문제를 해결하기 위해 설계된 공분산 및 보편성 메커니즘을 대체하는 표준 변수보다 바람직하다고 생각합니다.)

[엑스].일치 규칙에 반영된 단일 키워드를 추가하는 등 언어에 대한 사소한 변경이 필요하며 심각한 구현 어려움이 없습니다.

[엑스].(적어도 이론상으로는) 현실적입니다. 공변 재정의를 고정 재선언으로 대체하여 이전에 가능했던 모든 시스템을 다시 작성할 수 있습니다. 결과적으로 일부 조인이 유효하지 않은 것은 사실이지만 유형 위반으로 이어질 수 있는 경우에 해당하므로 할당 시도로 대체되고 런타임에 처리되어야 합니다.

논의는 여기서 마무리해도 될 것 같습니다. 그렇다면 강화 접근 방식에 완전히 만족하지 못하는 이유는 무엇입니까? 우선, 우리는 아직 아이에게 숨어있는 문제를 다루지 않았습니다. 또한, 논의를 계속하는 주된 이유는 이미 타입 변수에 대한 간략한 언급에서 표현된 문제 때문이다. 영향권을 다형성 부분과 공변 부분으로 나누는 것은 얄타 회의의 결과와 다소 유사합니다. 이는 클래스 설계자가 자신이 소개하는 각 개체, 특히 각 인수에 대해 두 가지 가능성 중 하나를 완전히 선택할 수 있는 특별한 직관을 가지고 있다고 가정합니다.

[엑스].엔터티는 잠재적으로 다형성입니다. 지금이나 나중에(매개변수 전달이나 할당을 통해) 선언된 유형과 다른 유형의 객체에 연결될 수 있습니다. 원래 엔터티 유형은 클래스의 하위 항목에 의해 변경될 수 없습니다.

[엑스].엔터티는 유형 재정의의 대상입니다. 즉, 고정되거나 그 자체가 지원 요소입니다.

하지만 개발자는 이 모든 것을 어떻게 예상할 수 있습니까? Open-Closed 원칙으로 주로 표현되는 OO 방법의 전체 매력은 이전에 수행한 작업에 대해 우리가 변경할 수 있는 권리가 있는 변경 가능성 및 범용 솔루션 개발자가 아니다자신의 제품이 후손의 필요에 어떻게 맞춰질 수 있는지 이해하는 무한한 지혜가 있어야 합니다.

이 접근 방식을 사용하면 유형 재정의 및 하위 숨기기가 일종의 "안전 밸브"가 되어 기존 클래스를 재사용할 수 있으며 목표 달성에 거의 적합합니다.

[엑스].유형 재정의를 사용하면 원본에 영향을 주지 않고 파생 클래스의 선언을 변경할 수 있습니다. 이 경우 순수 공변 솔루션에서는 설명된 변환을 사용하여 원본을 편집해야 합니다.

[엑스].자식 옆에 숨겨두면 클래스를 만들 때 많은 실패를 방지할 수 있습니다. 다음과 같은 프로젝트를 비판할 수 있습니다. 직사각형, 그 사람이라는 사실을 이용해서후손이다 다각형, 정점을 추가하려고 합니다. 대신 고정된 개수의 꼭지점을 가진 도형을 다른 도형과 분리하는 상속 구조를 제안할 수 있으며 문제가 발생하지 않습니다. 그러나 상속 구조를 개발할 때, 분류학적 예외. 하지만 완전히 없앨 수 있을까요? 나중에 강의에서 수출 제한에 대해 논의할 때 이것이 두 가지 이유로 불가능하다는 것을 알게 될 것입니다. 첫 번째는 경쟁적인 분류 기준이 존재한다는 것입니다. 둘째, 개발자가 완벽한 솔루션이 존재하더라도 이를 찾지 못할 가능성이 있습니다.

글로벌 분석

이 섹션에서는 중간 접근 방식을 설명합니다. 주요 실제 솔루션은 강의 17에서 제시됩니다.

고정 옵션을 연구하는 동안 우리는 그 주요 아이디어가 공변 및 다형성 엔터티 세트를 분리하는 것임을 알았습니다. 따라서 다음 형식의 두 가지 명령을 취하면


각각은 중요한 OO 메커니즘을 올바르게 적용한 예입니다. 첫 번째는 다형성이고 두 번째는 유형 재정의입니다. 문제는 동일한 엔터티에 대해 결합할 때 시작됩니다. 에스. 비슷하게:


문제는 두 명의 독립적이고 완전히 무고한 운영자의 조합으로 시작됩니다.

잘못된 호출은 유형 위반으로 이어집니다. 첫 번째 예에서는 다형성 할당이 객체를 추가합니다. 소년요점까지 에스, 그 사람 뭐하는 거야? g잘못된 인수 공유하다, 객체와 연관되어 있기 때문에 소녀. 엔터티에 대한 두 번째 예에서는 아르 자형객체 조인 직사각형, 이는 제외됩니다. add_vertex내보낸 구성요소 중에서

새로운 솔루션에 대한 아이디어는 다음과 같습니다. 사전에 - 정적으로, 컴파일러나 다른 도구로 유형을 확인할 때 - 우리는 정의합니다. 조판하다런타임 시 엔터티와 연결될 수 있는 개체 유형을 포함한 각 엔터티. 그런 다음 다시 정적으로 대상 유형 및 인수 세트의 각 요소에 대해 각 호출이 올바른지 확인합니다.

우리의 예에서 연산자는 s:=b클래스를 나타냅니다. 소년유형 집합에 속합니다. 에스(생성 명령을 실행한 결과 b를 생성하다이는 유형 세트에 속합니다. ). 소녀, 지침이 있기 때문에 g를 생성하다는 다음에 대한 유형 세트에 속합니다. g. 하지만 그때 도전은 공유하다그 목적으로는 받아들일 수 없을 것이다 에스유형 소년그리고 논쟁 g유형 소녀. 비슷하게 직사각형다음으로 설정된 유형입니다. , 이는 다형성 할당으로 인한 것입니다. 그러나 호출은 add_vertex을 위한 유형 직사각형받아들일 수 없는 것으로 판명될 것입니다.

이러한 관찰은 우리가 창조에 대해 생각하게 만듭니다. 글로벌새로운 입력 규칙을 기반으로 한 접근 방식:

시스템 정확성 규칙

부르다 x.f(인수)클래스가 올바른 경우에만 시스템이 정확합니다. 엑스, 그리고 인수, 해당 유형 세트의 모든 유형을 갖습니다.

이 정의에서 호출은 구성 요소 호출 규칙을 위반하지 않는 경우 클래스가 올바른 것으로 간주됩니다. 유형의 기본 클래스가 있습니다 엑스, 요소 에프수출해야 함 을 입력하고 인수형식 매개변수의 유형과 호환되어야 합니다. 에프. (기억하세요: 단순화를 위해 각 루틴에 하나의 매개변수만 있다고 가정하지만 규칙을 임의 개수의 인수로 확장하는 것은 어렵지 않습니다.)

호출의 시스템 정확성은 개별 요소가 아닌 집합 집합의 쌍에 대해 확인된다는 점을 제외하고 클래스 정확성으로 감소됩니다. 각 엔터티에 대한 유형 집합을 만드는 기본 규칙은 다음과 같습니다.

1 각 엔터티에 대해 초기 유형 집합은 비어 있습니다.

2 다음 형식의 다른 명령을 만난 경우 생성(SOME_TYPE)a, 추가하다 SOME_TYPE유형 집합으로 . (단순화를 위해 모든 명령이 다음과 같이 가정됩니다. 만들기지침으로 대체됩니다. 생성(ATYPE)a, 어디 유형- 엔터티 유형 .)

3 양식의 다른 할당이 발생했습니다. a:=b, 유형 세트에 추가 .

4 만약에 서브루틴의 형식적 매개변수가 있고 실제 매개변수가 있는 다음 호출이 발생합니다. , 유형 세트에 추가 다음에 대해 설정된 유형의 모든 요소 .

5 유형 집합이 더 이상 변경되지 않을 때까지 단계 (3)과 (4)를 반복합니다.

이 공식은 보편성의 메커니즘을 고려하지 않지만 필요에 따라 문제 없이 규칙을 확장할 수 있습니다. (5) 단계는 할당 및 이전 체인의 가능성으로 인해 필요합니다. 에게 , 에서 에게 등.). 제한된 수의 단계 후에 이 프로세스가 중지된다는 것을 이해하는 것은 어렵지 않습니다.

이미 알고 계시겠지만, 이 규칙은 지시사항의 순서를 고려하지 않습니다. 언제


생성(TYPE1)t; s:=t; 생성(TYPE2) t

유형 집합으로 에스다음과 같이 들어올 것이다 TYPE1, 그래서 TYPE2, 하지만 에스, 일련의 지침이 주어지면 첫 번째 유형의 값만 허용할 수 있습니다. 명령어의 위치를 ​​고려하면 컴파일러는 명령어 흐름을 심층적으로 분석해야 하며, 이로 인해 알고리즘의 복잡성 수준이 과도하게 증가하게 됩니다. 대신, 보다 비관적인 규칙이 적용됩니다. 작업 순서는 다음과 같습니다.


실행 순서가 유형 위반으로 이어지지 않음에도 불구하고 시스템이 올바르지 않은 것으로 선언됩니다.

시스템에 대한 전체적인 분석은 논문의 22장에 (더 자세히) 제시되었습니다. 동시에 공분산 문제와 상속 중 수출 제한 문제가 모두 해결되었습니다. 그러나 이 접근 방식에는 성가신 실제적인 결함이 있습니다. 즉, 검사를 가정한다고 가정합니다. 시스템 전체, 그리고 각 클래스가 별도로 있는 것은 아닙니다. 킬러는 라이브러리 서브루틴을 호출할 때 다른 클래스에서 가능한 모든 호출을 고려하는 규칙 (4)로 밝혀졌습니다.

이후 개별 클래스를 사용하는 알고리즘이 제안되었지만 실제 가치는 확립되지 않았습니다. 이는 증분 컴파일을 지원하는 프로그래밍 환경에서는 전체 시스템을 테스트해야 함을 의미합니다. 사용자가 일부 클래스에 적용한 변경 사항을 (빠른) 로컬로 처리하는 요소로 검사를 도입하는 것이 좋습니다. 예를 들어 전역 접근 방식의 사용 예가 알려져 있지만 C 프로그래머는 도구를 사용합니다. 보풀컴파일러가 감지하지 못한 시스템의 불일치를 찾는 것-이 모든 것이 그다지 매력적으로 보이지 않습니다.

결과적으로 내가 아는 한 시스템 정확성 검사는 누구도 구현하지 못했습니다. (이 결과의 또 다른 이유는 검증 규칙 자체의 복잡성 때문일 수 있습니다.)

클래스 정확성에는 클래스에 대한 검사가 포함되므로 증분 컴파일을 통해 가능합니다. 시스템 정확성은 증분 컴파일과 충돌하는 전체 시스템의 글로벌 검사를 전제로 합니다.

그러나 이름에도 불구하고 (일반 컴파일러를 실행하는 동안) 증분 클래스 검사만 사용하여 시스템 정확성을 검사하는 것이 실제로 가능합니다. 이것이 문제 해결에 대한 최종 기여가 될 것입니다.

다형성 캣콜을 조심하세요!

시스템 정확성 규칙은 비관적입니다. 단순성을 위해 완전히 안전한 명령 조합을 거부합니다. 역설적이게도 우리는 다음을 기반으로 마지막 솔루션을 구축할 것입니다. 훨씬 더 비관적인 규칙. 당연히 이는 우리의 결과가 얼마나 현실적일지에 대한 의문을 제기할 것입니다.

얄타로 돌아가기

솔루션의 본질 캣콜, - 우리는 나중에 이 개념의 의미를 설명할 것입니다. - 얄타 협정의 정신으로 돌아가서 세계를 다형성과 공변으로 나누지만(공분산의 동반자는 후손을 숨기는 것임) 소유할 필요가 없습니다. 무한한 지혜.

이전과 마찬가지로 공분산 문제를 두 가지 작업으로 좁혀 보겠습니다. 주요 예에서 이는 다형성 할당입니다. s:=b, 공변 서브루틴을 호출합니다. s.share(g). 위반의 실제 범인이 누구인지 분석하여 주장을 배제하겠습니다. g피의자 중에서. 유형의 모든 인수 스키 타는 사람또는 그것으로부터 파생된 것은 다형성으로 인해 우리에게 적합하지 않습니다 에스공분산 공유하다. 따라서 엔터티를 정적으로 설명하면 다른어떻게 스키 타는 사람객체에 동적으로 연결 스키 타는 사람, 그런 다음 전화 s.share(기타)정적으로는 이상적인 옵션이라는 인상을 주지만 다형성으로 할당하면 유형 위반이 발생합니다. 에스의미 .

근본적인 문제는 우리가 사용하려고 한다는 것입니다. 에스두 가지 호환되지 않는 방식으로: 다형성 엔터티로 사용되는 것과 공변 서브루틴 호출의 대상으로 사용됩니다. (다른 예에서 문제는 다음을 사용하는 것입니다. 다형성 엔터티 및 구성 요소를 숨기는 하위 서브루틴 호출의 대상 add_vertex.)

Consolidation과 같은 Catcall의 솔루션은 급진적입니다. 즉, 엔터티를 다형성 및 공변으로 사용하는 것을 금지합니다. 전역 구문 분석과 마찬가지로 어떤 엔터티가 다형성일 수 있는지 정적으로 결정하지만 엔터티에 대해 가능한 유형 집합을 찾아 너무 영리하게 노력하지는 않습니다. 대신, 모든 다형성 개체는 충분히 의심스러운 것으로 인식되며 공분산 및 후손에 의한 숨김을 포함하여 존경할만한 사람들과 동맹을 맺는 것이 금지됩니다.

하나의 규칙과 여러 정의

Catcall을 해결하기 위한 유형 규칙은 간단한 공식을 갖습니다.

Catcall에 대한 유형 규칙

다형성 캣콜은 올바르지 않습니다.

이는 똑같이 간단한 정의를 기반으로 합니다. 우선 다형성 실체는 다음과 같습니다.

정의: 다형성 실체

본질 엑스참조(확장되지 않음) 유형은 다음 속성 중 하나를 갖는 경우 다형성입니다.

1 할당에서 발생 x:=y, 엔터티는 어디에 있나요? 와이다른 유형을 가지거나 재귀에 의해 다형성입니다.

2 생성 지침에서 발견됨 생성(OTHER_TYPE)x, 어디 OTHER_TYPE선언에 지정된 유형이 아닙니다. 엑스.

3 서브루틴에 대한 공식적인 인수입니다.

4 외부 기능입니다.

이 정의의 목적은 프로그램 실행 중에 다양한 유형의 객체에 연결될 수 있는 모든 엔터티에 다형성("잠재적으로 다형성") 상태를 부여하는 것입니다. 확장된 엔터티는 본질적으로 다형성이 될 수 없기 때문에 이 정의는 참조 유형에만 적용됩니다.

우리의 예에서 스키어는 에스그리고 다각형 - 규칙 (1)에 따른 다형성. 그 중 첫 번째 개체가 할당됩니다. 보이비, 두 번째 - 개체 직사각형.

유형 집합 개념의 공식화에 익숙하다면 다형성 엔터티의 정의가 얼마나 비관적인지, 확인하기가 얼마나 쉬운지 알 수 있습니다. 엔터티의 가능한 모든 동적 유형을 찾으려고 노력하지 않고, 우리는 주어진 엔터티가 다형성일 수 있는지 아닌지에 대한 일반적인 질문에 만족합니다. 가장 놀라운 규칙은 (3)이다. 다형성카운트 각 형식 매개변수(정수 등의 경우처럼 유형이 확장되지 않는 한) 우리는 문제를 분석하는 데 신경도 쓰지 않습니다. 서브루틴에 인수가 있는 경우 전적으로 클라이언트가 처리할 수 있습니다. 이는 선언에 지정된 유형을 신뢰할 수 없음을 의미합니다. 이 규칙은 다음과 밀접한 관련이 있습니다. 재사용- 객체 기술의 목적 - 모든 클래스가 잠재적으로 라이브러리에 포함될 수 있으며 다른 클라이언트에 의해 반복적으로 호출됩니다.

이 규칙의 특징은 전역 검사가 필요하지 않다는 것입니다. 엔터티의 다형성을 식별하려면 클래스 자체의 텍스트를 보는 것으로 충분합니다. 모든 쿼리(속성 또는 기능)에 대해 다형성 상태에 대한 정보가 저장되어 있으면 조상 텍스트를 연구할 필요조차 없습니다. 유형 집합을 찾는 것과 달리 증분 컴파일 프로세스에서 클래스별로 확인하여 다형성 엔터티를 발견할 수 있습니다.

호출은 엔터티와 마찬가지로 다형성이 가능합니다.

정의: 다형성 호출

대상이 다형성이면 호출은 다형성입니다.

우리 예제의 두 호출 모두 다형성입니다. s.share(g)다형성으로 인해 에스, p.add_vertex(...)다형성으로 인해 . 정의에 따르면, 자격을 갖춘 호출만 다형성이 될 수 있습니다. (부적절한 전화를 거는 것 에프(...)일종의 자격을 갖춘 현재.f(...), 우리는 문제의 본질을 바꾸지 않습니다. 현재의는 아무것도 할당할 수 없으며 다형성 개체가 아닙니다.)

다음으로 CAT 개념을 기반으로 한 Catcall 개념이 필요합니다. (CAT는 가용성 또는 유형 변경의 약어입니다.) 서브루틴은 하위 항목에 의한 재정의로 인해 잠재적으로 위험한 두 가지 변경 중 하나가 발생하는 경우 CAT 서브루틴입니다. 즉, 인수 유형을 공변적으로 변경하거나 이전에 내보낸 구성 요소를 숨기는 것입니다.

정의: CAT 루틴

루틴을 재정의하여 내보내기 상태나 인수 유형을 변경하는 경우 루틴을 CAT 루틴이라고 합니다.

이 속성은 다시 증분 테스트의 대상이 됩니다. 인수 유형이나 내보내기 상태를 재정의하면 프로시저나 함수가 CAT 서브루틴이 됩니다. 이는 실패할 수 있는 CAT 루틴에 대한 호출인 Catcall의 개념으로 이어집니다.

정의: Catcall

내보내기 상태나 인수 유형의 변경으로 인해 일부 서브루틴 재정의로 인해 실패하게 되는 경우 해당 호출을 Catcall이라고 합니다.

우리가 만든 분류를 통해 특수 호출 그룹, 즉 다형성 및 캣콜을 식별할 수 있습니다. 다형성 호출은 객체 기반 접근 방식에 강력한 표현력을 추가하는 반면, catcall을 사용하면 유형을 재정의하고 내보내기를 제한할 수 있습니다. 이 강의 초반에 소개된 용어를 사용하여 다형성 호출은 다음과 같이 확장됩니다. 유용성, 고양이 호출 - 유용성.

도전과제 공유하다그리고 add_vertex, 우리의 예에서 논의된 것은 고양이 호출입니다. 첫 번째는 인수의 공변적 재정의를 수행합니다. 두 번째 것은 클래스에 의해 내보내집니다. 직사각형, 그러나 클래스에 의해 숨겨짐 다각형. 두 호출 모두 다형성이므로 서비스를 제공합니다. 좋은 예다형성 캣콜. Catcall 유형 규칙에 따르면 오류가 있습니다.

등급

공분산과 자식 숨기기에 대해 배운 모든 내용을 종합하기 전에 시스템의 정확성 위반이 실제로 드물다는 점을 다시 한 번 기억해 보겠습니다. 정적 객체 지향 타이핑의 가장 중요한 속성은 강의 시작 부분에 요약되었습니다. 클래스 정확성 검사와 결합된 이 인상적인 유형 조작 메커니즘 배열은 안전하고 유연한 소프트웨어 구축 방법을 열어줍니다.

우리는 공분산 문제에 대한 세 가지 해결책을 확인했으며 그 중 두 가지는 수출 제한 문제도 해결했습니다. 어느 것이 맞나요?

이 질문에 대한 확실한 대답은 없습니다. OO 타이핑과 다형성의 교묘한 상호작용이 암시하는 바는 이전 강의에서 제시된 문제만큼 잘 이해되지 않았습니다. 최근 몇 년 동안 이 주제에 관한 수많은 출판물이 나왔고, 이에 대한 참고문헌은 강의 마지막에 있는 참고문헌에 나와 있습니다. 더욱이, 이번 강의를 통해 최종 해결책의 요소를 제시했거나 적어도 그에 더 가까워졌기를 바랍니다.

글로벌 분석은 다음과 같은 이유로 실용적이지 않은 것으로 보입니다. 전체 확인전체 시스템. 그러나 문제를 더 잘 이해하는 데 도움이 되었습니다.

Pinning 솔루션은 매우 매력적입니다. 간단하고 직관적이며 구현하기 쉽습니다. 더욱이 우리는 개방-폐쇄 원칙에 반영된 OO 방법의 여러 핵심 요구 사항을 지원할 수 없다는 점을 후회해야 합니다. 우리가 정말로 직관력이 뛰어나다면 고정은 훌륭한 솔루션이겠지만, 어떤 개발자가 감히 이렇게 말하거나 자신의 프로젝트에서 상속받은 라이브러리 클래스의 작성자가 그러한 직관을 가지고 있다고 인정할 수 있을까요?

강제로 통합을 포기해야 한다면 Catcall 솔루션이 가장 적합하고 설명하기 쉽고 실제로 적용할 수 있는 솔루션인 것 같습니다. 그의 비관주의는 유용한 연산자 조합을 배제해서는 안 됩니다. "합법적인" 연산자에 의해 다형성 캣콜이 생성되는 경우 할당 시도를 도입하여 이를 안전하게 인정하는 것이 항상 가능합니다. 따라서 여러 검사를 프로그램 런타임으로 전송할 수 있습니다. 그러나 그러한 경우의 수는 극히 적어야 합니다.

명확히 하자면, 이 글을 쓰는 시점에는 Catcall 솔루션이 구현되지 않았습니다. 컴파일러가 Catcall 유형 규칙을 확인하도록 조정되고 크고 작은 표현 시스템에 성공적으로 적용될 때까지 정적 유형 지정과 공분산 및 하위 숨김이 결합된 다형성을 조화시키는 문제에 대해 마지막 말이 언급되기에는 너무 이릅니다. .

완전한 규정 준수

공분산에 대한 논의를 마무리하려면, 일반적인 방법이 상당히 일반적인 문제에 어떻게 적용될 수 있는지 이해하는 것이 유용합니다. 이 방법은 Catcall 이론의 결과로 등장했지만 새로운 규칙을 도입하지 않고도 언어의 기본 버전 프레임워크 내에서 사용할 수 있습니다.

두 개의 조정된 목록이 있다고 가정합니다. 첫 번째 목록은 스키어를 지정하고 두 번째 목록은 첫 번째 목록의 스키어에 대한 룸메이트를 지정합니다. 우리는 적절한 배치 절차를 따르고 싶습니다 공유하다, 이는 소녀가 소녀와 정착하고, 상을 받은 소녀와 상을 받은 소녀 등을 허용하는 유형 설명 규칙에 의해 허용되는 경우에만 가능합니다. 이러한 유형의 문제는 일반적입니다.

이전 논의와 과제 시도를 바탕으로 간단한 해결책이 있을 수 있습니다. 범용 기능을 고려하십시오. 장착(승인하다):


Fitted (other: GENERAL): 다른 것과 같습니다
-- 현재 개체(Current)(해당 유형이 개체 유형과 일치하는 경우)
-- 다른 것에 첨부되거나 그렇지 않으면 무효입니다.
if other /= Void이고 다음은 match_to (기타) then

기능 장착현재 객체를 반환하지만 인수에 연결된 유형의 엔터티로 알려져 있습니다. 현재 객체의 유형이 인수에 첨부된 객체의 유형과 일치하지 않는 경우 무효의. 할당 시도의 역할에 유의하세요. 이 함수는 구성요소를 사용합니다. 적합하다수업에서 일반적인, 이는 객체 쌍의 유형 호환성을 결정합니다.

대사 적합하다다른 구성 요소에 일반적인이름 포함 동일 유형우리에게 기능을 제공합니다 완벽하게 피팅됨 (완전한 준수), 이는 다음을 반환합니다. 무효의, 두 개체의 유형이 동일하지 않은 경우.

기능 장착- 유형 설명 규칙을 위반하지 않고 스키어 매칭 문제에 대한 간단한 솔루션을 제공합니다. 그래서 클래스 코드에서 스키 타는 사람새로운 절차를 도입하고 대신 사용할 수 있습니다. 공유하다, (후자는 숨겨진 절차로 수행될 수 있습니다).


-- 가능하다면 다른 사람을 번호로 이웃으로 선택하세요.
--gender_ascertained - 할당된 성별
gender_assertained_other: 현재와 같음
gender_ascertained_other:= 기타 .fitted(현재)
sex_assertained_other /= 무효인 경우
공유(gender_assertained_other)
"결론: 타인과의 공동 배치는 불가능하다"

을 위한 다른임의 유형 스키 타는 사람(뿐만 아니라 현재처럼) 버전을 결정 성별 확인_기타, 유형이 다음에 할당됨 현재의. 이 함수는 유형의 동일성을 보장하는 데 도움이 됩니다. 완벽하게 피팅됨.

계획된 숙박 시설을 대표하는 두 개의 평행 스키어 목록이 있는 경우:


점유자1, 점유자2: 목록

각 단계에서 호출하여 루프를 구성할 수 있습니다.


occupant1.item.safe_share (occupant2.item)

유형이 완전히 호환되는 경우에만 목록 요소를 일치시킵니다.

주요 개념

[엑스].정적 타이핑은 신뢰성, 가독성 및 효율성의 핵심입니다.

[엑스].현실적으로 정적 타이핑에는 어설션, 다중 상속, 할당 시도, 제한적 및 무제한적 다양성, 고정된 선언 등의 메커니즘 조합이 필요합니다. 유형 시스템은 트랩(유형 캐스트)을 허용해서는 안 됩니다.

[엑스].재선언에 대한 경험적 규칙은 공변적 재정의를 허용해야 합니다. 재정의된 결과 및 인수 유형은 원본 유형과 호환되어야 합니다.

[엑스].공분산은 물론 조상이 내보낸 구성 요소를 자손이 숨길 가능성도 다형성과 결합되어 드물지만 매우 심각한 유형 위반 문제를 야기합니다.

[엑스].이러한 위반은 다음을 사용하여 방지할 수 있습니다. 전역 분석(비실용적), 공분산을 고정 유형으로 제한(개방 폐쇄 원칙에 위배됨), 다형성 대상이 공분산을 사용하여 서브루틴을 호출하거나 숨는 것을 방지하는 Catcall 솔루션 아이.

참고문헌

본 강의의 다수의 자료는 OOPSLA 95 및 TOOLS PACIFIC 95 포럼에 보고서로 발표되었으며, 다음에도 출판되었습니다. 기사에서 다수의 리뷰 자료를 빌려왔습니다.

자동 유형 추론의 개념은 함수형 언어 ML에 대한 유형 추론 알고리즘을 설명하는 에서 소개되었습니다. 다형성과 유형 검사 간의 관계는 에서 조사되었습니다.

Self 언어의 맥락에서 동적 유형 언어의 코드 효율성을 높이는 기술은 다음에서 찾을 수 있습니다.

전문가에게 큰 영향을 미친 프로그래밍 언어 유형에 대한 이론적 기사는 Luca Cardelli와 Peter Wegner가 작성했습니다. 람다 미적분학(참조)을 기반으로 구축된 이 연구는 많은 추가 연구의 기초가 되었습니다. Cardelli의 또 다른 기본 기사가 선행되었습니다.

ISE 튜토리얼에는 다형성, 공분산 및 하위 숨기기를 함께 사용하는 문제에 대한 소개가 포함되어 있습니다. 이 책의 초판에 대한 적절한 분석의 부족으로 인해 많은 비판적인 논의가 발생했습니다. 그 중 첫 번째는 Philippe Elinck의 학사 논문 "De la Conception-Programmation par Objets", Memoire de licence, Universite에서 언급한 내용이었습니다. Libre de Bruxelles (벨기에), 1988), 작품에 표현되어 있습니다. Cook의 논문은 공분산 문제와 관련된 몇 가지 예를 제공하고 이를 해결하려고 시도합니다. 공변 엔터티에 대한 표준 매개변수를 기반으로 한 솔루션은 TOOLS EUROPE 1992에서 Franz Weber에 의해 제안되었습니다. 클래스 정확성뿐만 아니라 시스템 정확성의 개념에 대한 정확한 정의가 제공되며 시스템의 완전한 분석을 사용하는 솔루션이 제안됩니다. Catcall의 솔루션은 에서 처음 제안되었습니다. 또한보십시오 .

Pinning 솔루션은 TOOLS EUROPE 1994 세미나에서 발표되었지만 당시에는 Pinning 솔루션의 필요성을 느끼지 못했습니다. - 광고 및 관련 호환성 제한. Paul Dubois와 Amiram Yehudai는 이러한 조건에서 공분산 문제가 여전히 남아 있음을 재빠르게 지적했습니다. 그들은 Reinhardt Budde, Karl-Heinz Sylla, Kim Walden 및 James McKim과 마찬가지로 이 강의를 작성하게 된 작업에서 근본적으로 중요한 많은 의견을 제시했습니다.

공분산 문제를 다루는 많은 문헌이 있습니다. 에서는 광범위한 참고문헌과 문제의 수학적 측면에 대한 개요를 모두 찾을 수 있습니다. OOP의 유형 이론에 대한 온라인 자료와 해당 작성자의 웹 페이지에 대한 링크 목록은 Laurent Dami의 페이지를 참조하세요. 공분산과 반공분산의 개념은 범주 이론에서 차용되었습니다. 소프트웨어 타이핑의 맥락에서 이러한 기능이 등장한 것은 Luca Cardelli 덕분입니다. Luca Cardelli는 80년대 초반 연설에서 이를 사용하기 시작했지만 80년대 후반까지 인쇄물에서는 사용하지 않았습니다.

표준 변수를 기반으로 하는 기술은 , , 에 설명되어 있습니다.

Contravariance는 Sather 언어로 구현되었습니다. 설명은 에 나와 있습니다.

중간 옵션도 가능하지만 다음과 같은 두 가지 주요 접근 방식이 있습니다.

  • 동적 타이핑: 각 통화가 완료될 때까지 기다린 후 결정을 내리세요.
  • 정적 타이핑: 일련의 규칙이 주어지면 실행 중에 유형 위반이 가능한지 소스 텍스트에서 결정합니다. 규칙에 따라 오류가 없음이 보장되는 경우 시스템이 실행됩니다.

이러한 용어는 설명하기 쉽습니다. 동적 타이핑유형 검사는 시스템이 실행되는 동안(동적으로) 발생하며, 정적 타이핑검사는 텍스트에 대해 정적으로 수행됩니다(실행 전).

정적 타이핑일반적으로 컴파일러에 할당되는 자동 검사가 포함됩니다. 결과적으로 우리는 다음과 같은 간단한 정의를 얻었습니다.

정의: 정적으로 유형이 지정된 언어

객체 지향 언어는 시스템 실행이 유형 위반으로 이어지지 않도록 보장하는 일관되고 컴파일러 검사 규칙 세트와 함께 제공되는 경우 정적으로 유형이 지정됩니다.

용어 " 강한입력 중" ( 강한). 이는 유형 위반이 전혀 없어야 하는 정의의 최후통첩 성격에 해당합니다. 또한 가능하다 약한 (약한) 모양 정적 타이핑, 규칙은 특정 위반을 완전히 제거하지 않고 제거합니다. 이런 의미에서 일부 객체 지향 언어는 정적으로 약한 형식입니다. 우리는 가장 강력한 유형화를 위해 싸울 것입니다.

B 동적으로 입력된 언어유형이 지정되지 않은 것으로 알려진 에는 유형 선언이 없으며 런타임 시 엔터티에 연결된 값을 가질 수 있습니다. 정적 유형 검사는 불가능합니다.

타이핑 규칙

우리의 OO 표기법은 정적으로 입력됩니다. 타입 규칙은 이전 강의에서 소개되었으며 세 가지 간단한 요구 사항으로 요약됩니다.

  • 각 엔터티나 함수를 선언할 때 해당 유형을 지정해야 합니다. 예를 들면 다음과 같습니다. 계정: 계정. 각 루틴에는 0개 이상의 형식 인수가 있으며 그 유형을 지정해야 합니다(예: put (x: G; i: INTEGER) ).
  • x:= y 할당과 y가 형식 인수 x에 대한 실제 인수인 서브루틴 호출에서 소스 y의 유형은 대상 x의 유형과 호환되어야 합니다. 호환성의 정의는 상속을 기반으로 합니다. B는 A의 하위 항목인 경우 A와 호환됩니다. 일반 매개변수에 대한 규칙으로 보완됩니다("상속 소개" 참조).
  • x.f(arg)를 호출하려면 f가 대상 유형 x에 대한 기본 클래스 구성 요소여야 하며, f는 호출이 나타나는 클래스로 내보내야 합니다(14.3 참조).

실재론

정적으로 유형이 지정된 언어의 정의는 매우 정확하게 제공되지만 그것만으로는 충분하지 않습니다. 유형 지정 규칙을 만들 때 비공식 기준이 필요합니다. 두 가지 극단적인 경우를 생각해 보겠습니다.

  • 절대적으로 올바른 언어, 구문적으로 올바른 모든 시스템은 유형도 정확합니다. 유형 선언 규칙은 필요하지 않습니다. 이러한 언어가 존재합니다(정수를 더하고 빼는 폴란드 표기법을 상상해 보세요). 불행하게도 이 기준을 충족하는 실제 보편적 언어는 없습니다.
  • 완전히 잘못된 언어는 기존 언어를 선택하고 다음을 만드는 입력 규칙을 추가하여 쉽게 만들 수 있습니다. 어느시스템이 잘못되었습니다. 정의에 따르면 언어는 유형이 지정됩니다. 규칙을 따르는 시스템이 없기 때문에 어떤 시스템도 유형 위반을 일으키지 않습니다.

우리는 첫 번째 유형의 언어라고 말할 수 있습니다 적합한, 하지만 쓸모 없는, 후자가 유용할 수 있지만 적합하지 않을 수 있습니다.

실제로 우리는 사용 가능하고 유용한 타입 시스템이 필요합니다. 즉, 계산 요구 사항을 충족할 만큼 강력하고 타이핑 규칙을 충족시키기 위해 복잡하게 만들지 않을 만큼 충분히 편리한 타입 시스템이 필요합니다.

그 언어를 말해보자 현실적인, 실제로 사용 가능하고 유용한 경우. 정의와 달리 정적 타이핑, 질문에 대한 범주적인 답변을 제공합니다. " 언어 X가 정적으로 유형이 지정되어 있습니까??", 사실주의의 정의는 부분적으로 주관적입니다.

이번 강의에서는 우리가 제안하는 표기법이 현실적인지 확인해 보겠습니다.

염세주의

정적 타이핑본질적으로 "비관적" 정책으로 이어집니다. 이를 보장하려는 시도 모든 계산이 실패로 이어지지는 않습니다, 거부 오류 없이 끝날 수 있는 계산.

다양한 REAL 및 INTEGER 유형을 사용하는 일반적이고 객관적이지 않은 Pascal과 같은 언어를 생각해 보세요. n을 기술할 때: INTEGER; r: 실수 연산자 n:= r은 규칙을 위반하여 거부됩니다. 따라서 컴파일러는 다음 명령문을 모두 거부합니다.

n:= 0.0 [A] n:= 1.0 [B] n:= -3.67 [C] n:= 3.67 - 3.67 [D]

실행을 허용하면 [A]가 항상 작동한다는 것을 알 수 있습니다. 모든 숫자 시스템에는 명확하게 0 정수로 변환되는 실수 0,0의 정확한 표현이 있기 때문입니다. [B]도 거의 확실하게 작동할 것입니다. 작업 [C]의 결과는 명확하지 않습니다(소수 부분을 반올림하거나 버려서 결과를 얻고 싶습니까?). [D]는 운영자처럼 작업을 수행합니다.

n^2인 경우< 0 then n:= 3.67 end [E]

여기에는 도달할 수 없는 할당이 포함됩니다(n^2는 n의 제곱입니다). n^2를 n으로 바꾼 후에는 일련의 실행만으로 올바른 결과를 얻을 수 있습니다. 정수로 표현할 수 없는 큰 실수 값을 n에 할당하면 실패하게 됩니다.

안에 입력된 언어이러한 모든 예제(작동 중, 작동하지 않음, 때로는 작동함)는 무자비하게 유형 설명 규칙을 위반하는 것으로 간주되어 모든 컴파일러에서 거부됩니다.

질문은 그렇지 않습니다. 우리는 할 것이다우리가 비관주의자인지는 모르겠지만 사실은 얼마나 많이우리는 비관적일 여유가 있습니다. 현실성 요구 사항으로 돌아가 보겠습니다. 유형 규칙이 너무 비관적이어서 계산 작성의 용이성을 방해하는 경우 이를 거부합니다. 그러나 표현력을 약간 희생하여 형식 안전성을 달성한다면 우리는 이를 받아들일 것입니다. 예를 들어, 반올림 및 자르기 기능을 제공하는 개발 환경에서 n:= r 연산자는 기본 모호한 변환을 사용하는 대신 실수에서 정수로의 변환을 명시적으로 작성하도록 하기 때문에 잘못된 것으로 간주됩니다.

정적 타이핑: 방법과 이유

비록 혜택은 정적 타이핑당연한 얘기지만 다시 얘기해 보는 게 좋을 것 같아요.

장점

사용 이유 정적 타이핑객체 기술에 대해서는 강의 시작 부분에 나열했습니다. 이는 신뢰성, 이해 용이성 및 효율성입니다.

신뢰할 수 있음이는 작동 중에만, 일부 경우에만 나타날 수 있는 오류 감지로 인해 발생합니다. 함수뿐만 아니라 엔터티를 선언하도록 하는 첫 번째 규칙은 프로그램 텍스트에 중복성을 도입합니다. 이를 통해 컴파일러는 다른 두 규칙을 사용하여 엔터티, 구성 요소 및 구성 요소의 의도된 사용과 실제 사용 간의 불일치를 감지할 수 있습니다. 표현.

오류를 조기에 발견하는 것도 중요합니다. 오류를 찾는 시간이 길어질수록 수정 비용도 늘어나기 때문입니다. 모든 전문 프로그래머가 직관적으로 이해할 수 있는 이 속성은 Boehm의 잘 알려진 작업을 통해 정량적으로 확인됩니다. 오류를 찾는 시간에 대한 수정 비용의 의존성은 여러 대규모 산업 프로젝트의 데이터와 소규모 제어 프로젝트에서 수행된 실험을 기반으로 한 그래프에 표시됩니다.


쌀. 17.1.

가독성또는 이해하기 쉬운(가독성)에는 장점이 있습니다. 이 책의 모든 예에서 엔터티에 대한 유형의 모양은 독자에게 해당 목적에 대한 정보를 제공합니다. 유지 관리 단계에서는 가독성이 매우 중요합니다.

마지막으로, 능률실제로 사물기술의 성패를 좌우할 수 있다. 부재시 정적 타이핑 x.f(arg)는 완료하는 데 시간이 얼마든지 걸릴 수 있습니다. 그 이유는 런타임 시 대상 x의 기본 클래스에서 f를 찾을 수 없으면 해당 하위 항목에서 검색이 계속되므로 비효율성이 발생하기 때문입니다. 계층 구조에서 구성 요소 검색을 개선하면 문제를 완화할 수 있습니다. Self의 저자는 동적 유형 언어에 가장 적합한 코드를 생성하기 위해 많은 노력을 기울였습니다. 하지만 정확히는 정적 타이핑이러한 객체지향 제품은 효율성 면에서 기존 소프트웨어와 비슷하거나 동등할 수 있었습니다.

핵심은 정적 타이핑 x.f (arg) 구문에 대한 컴파일러 생성 코드가 x 유형을 알고 있다는 아이디어는 이미 언급되어 있습니다. 다형성으로 인해 f 구성 요소의 적절한 버전을 고유하게 결정할 수 있는 방법이 없습니다. 그러나 선언은 가능한 유형 세트의 범위를 좁혀 컴파일러가 최소한의 오버헤드로 올바른 f에 대한 액세스를 제공하는 테이블을 구성할 수 있도록 합니다. 제한된 상수로접근의 어려움. 추가 최적화 수행 정적 바인딩그리고 인라인- 덕분에 더 쉬워졌습니다. 정적 타이핑, 적용되는 비용을 완전히 제거합니다.

동적 타이핑의 경우

이 모든 것에도 불구하고, 동적 타이핑특히 스몰토크 프로그래머들 사이에서는 지지자들을 잃지 않습니다. 그들의 주장은 주로 위에서 논의한 현실주의에 기초하고 있습니다. 그들은 확신한다 정적 타이핑너무 많은 제약을 가해 창의적인 아이디어를 자유롭게 표현하는 것을 방해하고, 이를 '정조대'라고도 부르기도 합니다.

이러한 추론에 동의할 수 있지만 여러 기능을 지원하지 않는 정적으로 유형이 지정된 언어에만 해당됩니다. 유형 개념과 관련되고 이전 강의에서 소개된 모든 개념이 필요하다는 점은 주목할 가치가 있습니다. 그 중 하나를 거부하면 심각한 제한이 따르며 반대로 도입하면 행동 유연성이 제공되고 실용성을 마음껏 누릴 수 있는 기회 정적 타이핑.

타이핑: 성공의 구성요소

현실적인 메커니즘은 무엇입니까? 정적 타이핑? 모두 이전 강의에서 소개되었기 때문에 간략하게만 기억할 수 있을 뿐입니다. 이들을 함께 나열하면 연관성의 일관성과 힘이 드러납니다.

우리의 유형 시스템은 전적으로 다음 개념을 기반으로 합니다. 수업. INTEGER와 같은 기본 유형도 클래스이므로 미리 정의된 유형을 설명하기 위해 특별한 규칙이 필요하지 않습니다. (이것이 우리의 표기법이 이전 언어의 유형 시스템과 클래스 기반 개체 기술을 결합한 Object Pascal, Java 및 C++와 같은 "하이브리드" 언어와 다른 점입니다.)

확장형값이 객체를 나타내는 유형과 값이 참조를 나타내는 유형을 허용함으로써 더 많은 유연성을 제공합니다.

유연한 유형 시스템을 만드는 데 결정적인 단어는 다음과 같습니다. 계승그리고 이와 관련된 개념 호환성. 이는 x:= y 연산자에서 x와 y의 유형이 동일해야 하는 Pascal 및 Ada와 같은 고전적인 유형 언어의 주요 제한 사항을 극복합니다. 이 규칙은 너무 엄격합니다. 관련 유형(SAVINGS_ACCOUNT 및 CHECKING_ACCOUNT)의 개체를 나타낼 수 있는 엔터티의 사용을 금지합니다. 상속할 때 우리는 단지 유형 호환성 x 유형의 y. 예를 들어 x 유형은 ACCOUNT 이고 y는 SAVINGS_ACCOUNT 이며 두 번째 클래스는 첫 번째 클래스의 자손입니다.

실제로 정적으로 유형이 지정된 언어에는 지원이 필요합니다. 다중 상속. 근본적인 비난은 알려져 있습니다 정적 타이핑즉, 객체를 다르게 해석하는 것을 허용하지 않는다는 것입니다. 따라서 DOCUMENT 객체는 네트워크를 통해 전송될 수 있으므로 MESSAGE 유형과 관련된 구성 요소가 필요합니다. 하지만 이런 비판은 언어가 제한된 경우에만 해당됩니다. 단일 상속.


쌀. 17.2.

다재예를 들어 유연하지만 안전한 컨테이너 데이터 구조를 설명하는 데 필요합니다(예: 클래스 목록 [G] ...). 이 메커니즘이 없으면 정적 타이핑요소 유형이 다른 목록에 대해 다른 클래스를 선언해야 합니다.

경우에 따라 다용도가 필요함 한계, 일반 유형의 엔터티에만 적용되는 작업을 사용할 수 있습니다. 일반 클래스 SORTABLE_LIST가 정렬을 지원하는 경우 비교 연산자를 포함하려면 G 유형(여기서 G는 일반 매개변수)의 엔터티가 필요합니다. 이는 일반 제약 조건(COMPARABLE)을 지정하는 클래스를 G와 연결하여 달성됩니다.

클래스 SORTABLE_LIST ...

실제 일반 SORTABLE_LIST 매개변수는 필수 구성 요소가 있는 COMPARABLE 클래스의 하위 항목이어야 합니다.

또 다른 필수 메커니즘은 할당 시도- 소프트웨어가 제어하지 않는 유형의 개체에 대한 액세스를 구성합니다. y가 데이터베이스 개체이거나 네트워크를 통해 검색된 개체인 경우 연산자 x ?= y는 y가 호환 가능한 유형인 경우 x에 y 값을 할당하고, 그렇지 않은 경우 x에 Void 값을 제공합니다. .

진술계약에 의한 설계 아이디어의 일부로서, 전제조건, 사후조건 및 클래스 불변의 형태로 클래스 및 해당 구성요소와 연관되어 다루지 않는 의미론적 제약을 설명하는 것이 가능해집니다. 유형 사양. Pascal 및 Ada와 같은 언어에는 엔터티 값을 예를 들어 10에서 20으로 제한할 수 있는 범위 유형이 있지만 i가 음수인지, 항상 j 값의 두 배인지 확인할 수는 없습니다. 클래스 불변성은 아무리 복잡하더라도 부과된 제한 사항을 정확하게 반영하도록 설계되었습니다.

고정된 광고실제로 코드 중복이 발생하는 것을 방지하려면 필요합니다. 발표 y: x처럼, 하위 항목에서 x 유형을 반복적으로 선언하면 y가 변경된다는 보장을 받습니다. 이 메커니즘이 없으면 개발자는 다양한 유형 간의 일관성을 유지하기 위해 지속적으로 다시 선언하게 됩니다.

고정 선언은 우리가 요구하는 마지막 언어 메커니즘의 특별한 경우입니다. 공분산, 이에 대해서는 나중에 자세히 논의하겠습니다.

실제로 소프트웨어 시스템을 개발할 때 개발 환경 자체에 내재된 속성이 하나 더 필요합니다. 빠른 증분 재컴파일. 시스템을 작성하거나 수정할 때 변경 사항의 효과를 최대한 빨리 확인하고 싶을 것입니다. ~에 정적 타이핑유형을 다시 확인할 수 있도록 컴파일러에 시간을 주어야 합니다. 기존의 컴파일 루틴에서는 전체 시스템을 다시 번역해야 합니다(그리고 그 어셈블리), 이 프로세스는 특히 더 큰 규모의 시스템으로 이동할 때 매우 길어질 수 있습니다. 이 현상은 찬성론이 되었다 해석적초기 Lisp 또는 Smalltalk 환경과 같은 시스템은 처리나 유형 확인이 거의 또는 전혀 없이 시스템을 실행했습니다. 이 주장은 이제 잊혀졌습니다. 좋은 최신 컴파일러는 마지막 컴파일 이후 코드가 어떻게 변경되었는지 확인하고 발견된 변경 사항만 처리합니다.

“아기가 입력되었나요?”

우리의 목표 - 엄격한 정적 타이핑. 이것이 바로 우리가 "규칙에 따른 게임"에서 허점을 피해야 하는 이유입니다. 적어도 허점이 존재한다면 이를 정확하게 식별해야 합니다.

정적으로 가장 흔히 발생하는 허점 입력된 언어엔터티 유형을 변경하는 변환이 존재합니다. C 및 그 파생 언어에서는 이를 "유형 캐스팅" 또는 캐스팅이라고 합니다. (OTHER_TYPE) x 표기법은 x 값이 가능한 유형에 대한 특정 제한 사항에 따라 컴파일러에서 OTHER_TYPE 유형을 갖는 것으로 처리되도록 지정합니다.

이러한 메커니즘은 유형 검사의 한계를 우회합니다. 캐스팅은 ANSI C 방언을 포함한 C 프로그래밍에서 일반적입니다. C++에서도 유형 캐스팅은 흔하지는 않지만 여전히 일반적이고 필요할 수 있습니다.

규칙을 따르려면 정적 타이핑캐스팅을 통해 언제든지 우회할 수 있다면 그리 간단하지 않습니다.

입력 및 연결

이 책의 독자라면 아마도 정적 타이핑과 정적 타이핑을 구별할 것입니다. 제본, 이것을 할 수없는 사람들이 있습니다. 이는 부분적으로 Smalltalk 언어의 영향 때문일 수 있습니다. 역동적인 접근두 문제 모두에 대해 동일한 해결책을 가지고 있다는 오해를 형성할 수 있습니다. (저희 책에서는 안정적이고 유연한 프로그램을 만들려면 정적 타이핑과 동적 바인딩을 결합하는 것이 바람직하다고 주장합니다.)

타이핑과 바인딩 모두 기본 구문 x.f (arg) 의 의미를 다루지만 두 가지 다른 질문에 대답합니다.

입력 및 연결

  • 질문을 입력하는 중: 런타임 시 엔터티 x(매개변수 arg 사용)에 연결된 객체에 f에 해당하는 작업이 적용된다는 것을 언제 확실히 알아야 합니까?
  • 연결 질문: 주어진 호출이 어떤 작업을 시작하는지 언제 알아야 합니까?

입력하면 가용성 문제에 대한 답변이 제공됩니다. 적어도 하나작업, 바인딩은 선택을 담당합니다. 필요한.

객체 접근 내에서:

  • 타이핑에서 발생하는 문제는 다음과 관련이 있습니다. 다형성: x 이후 실행 중여러 가지 다른 유형의 객체를 나타낼 수 있으므로 f를 나타내는 연산이 다음과 같은지 확인해야 합니다. 사용 가능이 각각의 경우에;
  • 바인딩 문제 발생 반복되는 발표: 클래스는 상속된 구성 요소를 변경할 수 있으므로 주어진 호출에서 f를 표현한다고 주장하는 작업이 두 개 이상 있을 수 있습니다.

두 문제 모두 동적으로나 정적으로 해결될 수 있습니다. 기존 언어는 4가지 솔루션을 모두 제공합니다.

그리고 동적 바인딩은 이 책에서 제안하는 표기법으로 구현됩니다.

유형 캐스팅으로 인해 엄격하지는 않지만 정적 유형 지정을 지원하는 C++ 언어의 고유성에 주목해 보겠습니다. 정적 연결(기본값), virtual을 명시적으로 지정할 때 동적 바인딩( 가상) 광고.

선택 이유 정적 타이핑동적 연결이 분명합니다. 첫 번째 질문은 "구성 요소의 존재를 언제 알 수 있습니까?"입니다. - 정적인 답변을 제안합니다: " 빠를수록 좋다", 즉 컴파일 타임을 의미합니다. 두 번째 질문: "어떤 구성 요소를 사용해야 합니까?"에는 동적 답변이 필요합니다. " 당신이 필요로하는 것", - 해당 동적 유형런타임 시 결정되는 개체입니다. 이는 정적 바인딩과 동적 바인딩이 서로 다른 결과를 생성하는 경우 유일하게 허용되는 솔루션입니다.

~에 정적 타이핑프로그램이 실행될 때 my_aircraft 엔터티에 lower_landing_gear 에 해당하는 구성 요소와 함께 제공되는 개체가 첨부될 것이라고 보장할 수 있는 경우 컴파일러는 호출을 거부하지 않습니다. 보증을 얻는 기본 기술은 간단합니다. my_aircraft를 필수로 선언하려면 해당 유형의 기본 클래스에 이러한 구성 요소가 포함되어야 합니다. 따라서 my_aircraft는 해당 수준에 lower_landing_gear가 없기 때문에 AIRCRAFT로 선언할 수 없습니다. 적어도 우리의 예에서 헬리콥터는 랜딩 기어를 내리는 방법을 모릅니다. 엔터티를 다음과 같이 선언하면 비행기, - 필수 구성 요소가 포함된 클래스 - 모든 것이 정상입니다.

동적 타이핑 Smalltalk 스타일에서는 호출을 기다려야 하며 실행 시 필요한 구성 요소가 있는지 확인해야 합니다. 이 동작은 프로토타입 및 실험 개발에서는 가능하지만 산업용 시스템에서는 허용되지 않습니다. 비행 중에 랜딩 기어가 있는지 물어보기에는 너무 늦습니다.

이 기사에서는 정적으로 유형이 지정되는 언어와 동적으로 유형이 지정되는 언어의 차이점을 설명하고 "강한" 유형과 "약한" 유형의 개념을 조사하며 다음과 같은 유형의 유형 지정 시스템의 성능을 비교합니다. 다른 언어들. 최근에는 프로그래밍에서 더욱 엄격하고 강력한 타이핑 시스템을 향한 분명한 움직임이 있어왔으므로 이것이 무엇을 의미하는지 이해하는 것이 중요합니다. 우리 얘기 중이야유형과 유형화에 관해 이야기할 때.



유형은 가능한 값의 모음입니다. 정수는 0, 1, 2, 3 등의 값을 가질 수 있습니다. 부울은 true 또는 false일 수 있습니다. 예를 들어 가능한 값이 "high"와 "5"인 "High Five"유형과 같이 자신 만의 유형을 생각해 낼 수 있습니다. 문자열이나 숫자가 아니라 새로운 별도의 유형입니다.


정적으로 유형이 지정된 언어는 변수 유형을 제한합니다. 예를 들어 프로그래밍 언어는 x가 정수라는 것을 알 수 있습니다. 이 경우 프로그래머는 x = true를 수행하는 것이 금지됩니다. 이는 잘못된 코드가 됩니다. 컴파일러는 컴파일을 거부하므로 코드를 실행할 수도 없습니다. 정적으로 유형이 지정된 또 다른 언어는 표현 기능이 다를 수 있으며 널리 사용되는 유형 시스템 중 어느 것도 HighFive 유형을 표현할 수 없습니다(그러나 대부분은 다른 더 정교한 아이디어를 표현할 수 있습니다).


동적 유형 언어는 값을 유형으로 표시합니다. 언어는 1이 정수이고 2가 정수라는 것을 알고 있지만 변수 x가 항상 정수를 포함한다는 것을 알 수 없습니다.


언어 런타임은 다양한 시점에 이러한 레이블을 확인합니다. 두 개의 값을 더하려고 하면 그 값이 숫자인지, 문자열인지, 배열인지 확인할 수 있습니다. 그런 다음 유형에 따라 이러한 값을 추가하거나 서로 붙이거나 오류를 발생시킵니다.

정적으로 입력된 언어

정적 언어는 프로그램이 실행되기 전 컴파일 타임에 프로그램의 유형을 확인합니다. 유형이 언어 규칙을 위반하는 모든 프로그램은 잘못된 것으로 간주됩니다. 예를 들어, 대부분의 정적 언어는 "a" + 1이라는 표현을 거부합니다(C는 이 규칙의 예외입니다). 컴파일러는 "a"가 문자열이고 1이 정수라는 것을 알고 있으며 +는 왼쪽과 오른쪽이 동일한 유형일 때만 작동합니다. 따라서 문제가 있음을 깨닫기 위해 프로그램을 실행할 필요가 없습니다. 정적으로 유형이 지정된 언어의 각 표현식은 코드를 실행하지 않고도 확인할 수 있는 특정 유형입니다.


많은 정적으로 유형이 지정된 언어에는 유형 표시가 필요합니다. Java 함수 public int add(int x, int y)는 두 개의 정수를 취하고 세 번째 정수를 반환합니다. 다른 정적으로 유형이 지정된 언어는 유형을 자동으로 추론할 수 있습니다. Haskell의 동일한 덧셈 함수는 다음과 같습니다: add x y = x + y . 언어에 유형을 알려주지는 않지만 +는 숫자에만 작동하므로 x와 y는 숫자여야 하므로 add 함수는 두 숫자를 인수로 사용하므로 자체적으로 유형을 알아낼 수 있습니다.


이는 유형 시스템의 "정적" 특성을 감소시키지 않습니다. Haskell의 유형 시스템은 정적이고 엄격하며 강력한 것으로 유명하며 Haskell은 이러한 모든 면에서 Java보다 앞서 있습니다.

동적 유형 언어

동적 유형 언어는 유형을 지정할 필요가 없지만 자체적으로 정의하지는 않습니다. 변수 유형은 시작 시 특정 값을 가질 때까지 알 수 없습니다. 예를 들어, Python의 함수


def f(x, y): x + y를 반환합니다.

두 개의 정수를 추가하고 문자열, 목록 등을 연결할 수 있으며 프로그램을 실행하기 전까지는 정확히 무슨 일이 일어나고 있는지 이해할 수 없습니다. 함수 f가 어느 시점에서는 두 개의 문자열로 호출되고 다른 시점에서는 두 개의 숫자로 호출될 가능성이 있습니다. 이 경우 x와 y에는 서로 다른 시간에 서로 다른 유형의 값이 포함됩니다. 이것이 동적 언어의 값에는 유형이 있지만 변수와 함수에는 유형이 없다고 말하는 이유입니다. 값 1은 확실히 정수이지만 x와 y는 무엇이든 될 수 있습니다.

비교

대부분의 동적 언어는 유형이 잘못 사용되면 오류를 발생시킵니다(JavaScript는 주목할만한 예외입니다. 의미가 없는 경우에도 모든 표현식에 대해 값을 반환하려고 시도합니다). 동적 유형 언어를 사용하는 경우 프로덕션 환경에서는 "a" + 1과 같은 간단한 오류도 발생할 수 있습니다. 정적 언어는 이러한 오류를 방지하지만 물론 방지 정도는 유형 시스템의 강도에 따라 다릅니다.


정적 언어와 동적 언어는 프로그램 정확성에 대한 근본적으로 다른 아이디어를 기반으로 구축되었습니다. 동적 언어에서 "a" + 1은 유효한 프로그램입니다. 코드가 실행되고 런타임 환경에 오류가 나타납니다. 그러나 대부분의 정적인 유형의 언어에서는 "a" + 1이라는 표현이 프로그램이 아니다: 컴파일되지 않고 실행되지 않습니다. 이것은 임의의 문자 집합과 마찬가지로 잘못된 코드입니다!&%^@*&%^@*는 잘못된 코드입니다. 정확성과 부정확성에 대한 이 추가 개념은 동적 언어에서는 해당되지 않습니다.

강한 타이핑과 약한 타이핑

'강함'과 '약함'의 개념은 매우 모호합니다. 다음은 그 사용에 대한 몇 가지 예입니다.

    때때로 "강함"은 "정적"을 의미합니다.
    간단하지만 대부분의 사람들이 사용하고 이해하기 때문에 "정적"이라는 용어를 사용하는 것이 더 좋습니다.

    때때로 "강함"은 "암시적 유형 변환을 수행하지 않음"을 의미합니다.
    예를 들어 JavaScript를 사용하면 "a" + 1을 쓸 수 있는데, 이는 "약한 타이핑"이라고 할 수 있습니다. 그러나 거의 모든 언어는 정수에서 1 + 1.1과 같은 부동 소수점 숫자로 자동 변환할 수 있는 일정 수준의 암시적 변환을 제공합니다. 실제로 대부분의 사람들은 허용 가능한 개종과 허용할 수 없는 개종 사이의 경계를 정의하기 위해 "강한"이라는 단어를 사용합니다. 일반적으로 인정되는 경계는 없으며 모두 부정확하며 특정 사람의 의견에 따라 다릅니다.

    때때로 "강하다"는 것은 언어의 엄격한 타이핑 규칙을 우회하는 것이 불가능하다는 것을 의미합니다.

  • 때때로 "강함"은 메모리 안전을 의미합니다.
    C는 메모리에 안전하지 않은 언어의 예입니다. xs가 4개 숫자의 배열이면 C는 xs 또는 xs를 실행하여 xs 바로 뒤의 메모리에서 일부 값을 반환합니다.

그만하자. 일부 언어가 이러한 정의를 충족하는 방법은 다음과 같습니다. 보시다시피 Haskell만이 모든 면에서 일관되게 "강하다". 대부분의 언어는 그다지 명확하지 않습니다.



("암시적 변환" 열의 "언제"는 강한 것과 약한 것의 구분이 우리가 허용 가능한 것으로 간주하는 변환에 따라 달라진다는 것을 의미합니다).


종종 "강함"과 "약함"이라는 용어는 위의 다양한 정의와 여기에 표시되지 않은 정의의 모호한 조합을 나타냅니다. 이러한 모든 혼란으로 인해 "강함"과 "약함"이라는 단어가 거의 의미가 없게 됩니다. 이러한 용어를 사용하려면 정확히 무엇을 의미하는지 설명하는 것이 좋습니다. 예를 들어, "JavaScript는 숫자가 포함된 문자열을 추가하면 값을 반환하지만 Python은 오류를 반환합니다."라고 말할 수 있습니다. 이 경우, 우리는 "강하다"라는 단어의 다양한 의미에 대해 합의하려고 노력하는 데 에너지를 낭비하지 않을 것입니다. 또는 더 나쁜 것은 용어로 인해 해결되지 않은 오해로 끝날 것입니다.


대부분 인터넷에서 '강하다', '약하다'는 말은 특정 개인의 모호하고 제대로 정의되지 않은 의견이다. 그들은 언어를 "나쁜" 또는 "좋은"이라고 부르는 데 사용되며 이러한 의견은 기술적인 전문 용어로 변합니다.



강력한 타이핑(Strong Typing): 제가 좋아하고 편안하게 느끼는 유형 시스템입니다.

약한 타이핑: 나를 귀찮게 하거나 불편하게 만드는 유형 시스템입니다.

점진적인 타이핑

동적 언어에 정적 유형을 추가할 수 있습니까? 어떤 경우에는 그렇습니다. 다른 경우에는 어렵거나 불가능합니다. 가장 명백한 문제는 eval 및 기타 동적 언어의 유사한 기능입니다. Python에서 1 + eval("2")를 수행하면 3이 생성됩니다. 하지만 1 + eval(read_from_the_network())는 무엇을 생성합니까? 실행 당시 온라인 상태에 따라 다릅니다. 숫자를 얻으면 표현이 정확합니다. 문자열이라면 아니요입니다. 실행하기 전에 알 수 있는 방법이 없으므로 유형을 정적으로 구문 분석하는 것은 불가능합니다.


실제로 만족스럽지 못한 해결책은 eval() 표현식을 Any 유형으로 설정하는 것입니다. 이는 일부 객체 지향 프로그래밍 언어의 Object 또는 Go의 인터페이스()와 유사합니다. 어떤 값으로도 만족할 수 있는 유형입니다.


Any 유형의 값은 제한이 없으므로 평가 코드에 도움이 되는 유형 시스템의 기능이 사라집니다. eval과 타입 시스템을 모두 갖춘 언어는 eval을 사용할 때마다 타입 안전성을 포기해야 합니다.


일부 언어에는 선택적 또는 점진적 입력이 있습니다. 기본적으로 동적이지만 일부 정적 주석을 추가할 수 있습니다. Python은 최근 선택적 유형을 추가했습니다. TypeScript는 선택적 유형이 있는 JavaScript의 상위 집합입니다. Flow는 오래된 JavaScript 코드의 정적 분석을 수행합니다.


이러한 언어는 정적 타이핑의 이점 중 일부를 제공하지만 진정한 정적 언어에 대한 절대적인 보장은 제공하지 않습니다. 일부 함수는 정적으로 유형이 지정되고 일부는 동적으로 유형이 지정됩니다. 프로그래머는 항상 차이점을 인식하고 주의해야 합니다.

정적으로 유형이 지정된 코드 컴파일

정적으로 유형이 지정된 코드를 컴파일할 때 모든 컴파일러에서와 마찬가지로 구문을 먼저 확인합니다. 그런 다음 유형을 확인합니다. 이는 정적 언어가 처음에는 하나의 구문 오류에 대해 불평할 수 있으며, 이를 수정한 후에는 100개의 입력 오류에 대해 불평할 수 있음을 의미합니다. 구문 오류 수정으로 인해 100개의 입력 오류가 발생하지 않았습니다. 컴파일러는 구문이 수정될 때까지 유형 오류를 감지할 방법이 없었습니다.


정적 언어용 컴파일러는 일반적으로 동적 언어용 컴파일러보다 더 빠른 코드를 생성할 수 있습니다. 예를 들어 컴파일러가 add 함수가 정수를 허용한다는 것을 알고 있으면 CPU의 기본 ADD 명령어를 사용할 수 있습니다. 동적 언어는 유형에 따라 다양한 추가 함수(정수 또는 부동 소수점 추가, 문자열 또는 목록 연결 등) 중에서 선택하여 런타임에 유형을 확인하거나 오류가 발생했고 유형이 일치하지 않는지 결정해야 합니다. . 이 모든 확인에는 시간이 걸립니다. 동적 언어는 필요한 모든 유형 정보를 얻은 후 런타임에 코드를 다시 컴파일하는 JIT 컴파일(Just-In-Time)과 같은 최적화를 위해 다양한 트릭을 사용합니다. 그러나 어떤 동적 언어도 Rust와 같은 언어로 깔끔하게 작성된 정적 코드의 속도를 따라올 수 없습니다.

정적 및 동적 유형에 대한 인수

정적 유형 시스템을 지지하는 사람들은 유형 시스템이 없으면 단순한 오류로 인해 생산 시 문제가 발생할 수 있다고 지적합니다. 물론 이것은 사실이다. 동적 언어를 사용해 본 사람이라면 누구나 이를 직접 경험해 본 적이 있을 것입니다.


동적 언어를 지지하는 사람들은 그러한 언어가 코드를 작성하기 더 쉽다고 지적합니다. 이는 평가 코드와 같이 우리가 때때로 작성하는 일부 종류의 코드에는 확실히 해당됩니다. 이는 정규 작업에 대한 논란의 여지가 있는 솔루션이며 여기서 "쉬움"이라는 모호한 단어를 기억하는 것이 합리적입니다. Rich Hickey는 "쉬움"이라는 단어와 "단순함"이라는 단어와의 연관성에 대해 훌륭하게 설명했습니다. 이 보고서를 보고 나면 '쉬움'이라는 단어를 올바르게 사용하는 것이 쉽지 않다는 것을 이해하게 될 것입니다. "쉬움"을 조심하세요.


정적 및 정적의 장점과 단점 동적 시스템타이핑은 여전히 ​​​​잘 이해되지 않지만 언어와 해결되는 특정 문제에 따라 확실히 달라집니다.


JavaScript는 의미 없는 변환(예: "a" + 1로 인해 "a1"이 되는 경우)을 의미하더라도 계속하려고 합니다. 반면에 Python은 "a" + 1 의 경우처럼 보수적으로 노력하고 오류를 반환하는 경우가 많습니다.


다양한 접근 방식이 있습니다. 다양한 수준에서보안이 뛰어나지만 Python과 JavaScript는 모두 동적 유형 언어입니다.



하스켈은 먼저 명시적인 변환 없이 정수와 부동 소수점을 추가하는 것을 허용하지 않습니다. C와 Haskell은 이러한 큰 차이점에도 불구하고 둘 다 정적으로 유형이 지정됩니다.


동적 언어와 정적 언어에는 다양한 변형이 있습니다. "X에 관해서는 정적 언어가 동적 언어보다 낫다"와 같은 포괄적인 진술은 거의 말도 안 되는 것이 보장됩니다. 특정 언어의 경우에는 그럴 수도 있지만, "X에 있어서는 Haskell이 Python보다 낫다"고 말하는 것이 더 좋습니다.

다양한 정적 타이핑 시스템

정적으로 유형이 지정된 언어의 두 가지 유명한 예인 Go와 Haskell을 살펴보겠습니다. Go 타이핑 시스템에는 일반 유형, 즉 다른 유형의 "매개변수"가 있는 유형이 없습니다. 예를 들어, 필요한 데이터를 저장할 수 있는 MyList 목록에 대한 자체 유형을 만들 수 있습니다. 우리는 변경 없이 정수의 MyList, 문자열의 MyList 등을 생성할 수 있기를 원합니다. 원천나의 목록. 컴파일러는 입력을 감시해야 합니다. 정수로 구성된 MyList가 있고 실수로 거기에 문자열을 추가한 경우 컴파일러는 프로그램을 거부해야 합니다.


Go는 MyList와 같은 유형을 정의할 수 없도록 특별히 설계되었습니다. 할 수 있는 최선의 방법은 "빈 인터페이스"의 MyList를 생성하는 것입니다. MyList는 객체를 포함할 수 있지만 컴파일러는 해당 유형을 알지 못합니다. MyList에서 객체를 검색할 때 컴파일러에 객체 유형을 알려주어야 합니다. "문자열을 받고 있습니다"라고 말했지만 실제로 값이 숫자인 경우 동적 언어의 경우처럼 런타임 오류가 발생합니다.


또한 Go에는 현대의 정적 유형 언어(또는 1970년대 일부 시스템)에서 볼 수 있는 다른 기능도 많지 않습니다. Go를 만든 사람들은 이러한 결정을 내린 데에는 나름의 이유가 있었지만, 이 문제에 대한 외부인의 의견은 때때로 가혹하게 들릴 수 있습니다.


이제 매우 강력한 유형 시스템을 갖춘 Haskell과 비교해 보겠습니다. 유형을 MyList로 설정하면 "번호 목록"의 유형은 단순히 MyList Integer 입니다. Haskell은 실수로 목록에 문자열을 추가하는 것을 방지하고 목록의 요소를 문자열 변수에 넣지 않도록 보장합니다.


Haskell은 훨씬 더 복잡한 아이디어를 유형으로 직접 표현할 수 있습니다. 예를 들어 Num a => MyList a는 "동일한 유형의 숫자에 속하는 값의 MyList"를 의미합니다. 이는 정수, 부동 소수점 또는 십진수고정된 정밀도이지만 컴파일 타임에 확인되는 문자열 목록은 절대 아닙니다.


모든 숫자 유형에서 작동하는 추가 함수를 작성할 수 있습니다. 이 함수의 유형은 Num a => (a -> a -> a) 입니다. 그 뜻은:

  • a는 모든 숫자 유형일 수 있습니다(Num a =>).
  • 이 함수는 a 유형의 두 인수를 사용하고 a 유형(a -> a -> a)을 반환합니다.

마지막 예입니다. 함수 유형이 String -> String 이면 문자열을 허용하고 문자열을 반환합니다. 그러나 String -> IO String 이면 일부 I/O도 수행합니다. 여기에는 디스크 액세스, 네트워크 액세스, 터미널에서 읽기 등이 포함될 수 있습니다.


함수에 유형이 있는 경우 아니요 IO, 그러면 I/O 작업을 수행하지 않는다는 것을 알 수 있습니다. 예를 들어 웹 애플리케이션에서는 함수의 유형만 보면 함수가 데이터베이스를 수정하는지 여부를 알 수 있습니다. 동적 언어나 정적 언어는 거의 없습니다. 이것은 가장 강력한 타이핑 시스템을 갖춘 언어의 특징입니다.


대부분의 언어에서 우리는 데이터베이스를 변경하는 것을 찾으려고 함수와 거기에서 호출되는 모든 함수 등을 헤쳐나가야 합니다. 이는 지루한 과정이며 실수하기 쉽습니다. 그리고 하스켈 타입 시스템은 이 질문에 간단하고 확실하게 답할 수 있습니다.


이 힘을 "숫자와 동일한 유형의 두 인수를 취하여 I/O를 수행하는 함수"는 물론 MyList의 간단한 아이디어를 표현할 수 없는 Go와 비교해 보세요.


Go 접근 방식을 사용하면 Go 프로그래밍 도구를 더 쉽게 작성할 수 있습니다(특히 컴파일러 구현이 간단할 수 있음). 게다가 배워야 할 개념도 적습니다. 이러한 이점을 중요한 제한 사항과 비교하는 방법은 주관적인 질문입니다. 그러나 Haskell이 Go보다 배우기가 더 어렵고 Haskell의 유형 시스템이 훨씬 더 강력하며 Haskell이 컴파일할 때 더 많은 유형의 버그를 예방할 수 있다는 점에는 이의가 없습니다.


Go와 Haskell은 용어가 올바르게 사용되더라도 동일한 클래스의 "정적 언어"로 그룹화하는 것은 오해의 소지가 있을 수 있는 서로 다른 언어입니다. 실질적인 보안 이점을 비교할 때 Go는 Haskell보다 동적 언어에 더 가깝습니다.


반면에 일부 동적 언어는 일부 정적 언어보다 안전합니다. (Python은 일반적으로 C보다 훨씬 더 안전한 것으로 간주됩니다.) 정적 또는 동적 언어를 그룹으로 일반화하려면 다음을 잊지 마십시오. 엄청난 숫자언어 간의 차이점.

타이핑 시스템의 기능 차이에 대한 구체적인 예

더 강력한 타이핑 시스템은 더 작은 수준에서 제약 조건을 지정할 수 있습니다. 여기에 몇 가지 예가 있지만 구문이 명확하지 않더라도 너무 매달리지 마십시오.


Go에서는 "add 함수는 두 개의 정수를 취하고 정수를 반환합니다"라고 말할 수 있습니다.


func add(x int, y int) int ( x + y 반환)

Haskell에서는 "함수는 다음을 수행합니다. 어느숫자 유형이며 동일한 유형의 숫자를 반환합니다.":


f:: Num a => a -> a -> a 더하기 x y = x + y

Idris에서는 "함수는 두 개의 정수를 취하고 정수를 반환하지만 첫 번째 인수는 두 번째 인수보다 작아야 합니다"라고 말할 수 있습니다.


add: (x: Nat) -> (y: Nat) -> (자동 작아짐: LT x y) -> Nat add x y = x + y

첫 번째 인수가 두 번째 인수보다 큰 add 2 1 함수를 호출하려고 하면 컴파일러는 프로그램을 거부합니다. 컴파일 타임에. 첫 번째 인수가 두 번째 인수보다 큰 프로그램을 작성하는 것은 불가능합니다. 언어에 이러한 기능이 있는 경우는 거의 없습니다. 대부분의 언어에서 이 검사는 런타임에 발생합니다. if x >= y: raise SomeError() 와 같은 내용을 작성합니다.


위 Idris 예제의 유형에 해당하는 Haskell이 없으며 Haskell 또는 Idris 예제에 해당하는 Go도 없습니다. 결과적으로 Idris는 Haskell이 방지할 수 없는 많은 버그를 방지할 수 있고, Haskell은 Go가 알아차리지 못하는 많은 버그를 방지할 수 있습니다. 두 경우 모두 필요합니다. 추가 기능언어를 더욱 복잡하게 만드는 타이핑 시스템.

일부 정적 언어의 타이핑 시스템

다음은 파워가 증가하는 순서대로 일부 언어의 타이핑 시스템을 대략적으로 나열한 것입니다. 이 목록은 시스템의 힘에 대한 일반적인 아이디어를 제공하므로 이를 절대적인 진실로 취급하지 마십시오. 한 그룹에 모인 언어는 서로 매우 다를 수 있습니다. 모든 타이핑 시스템에는 고유한 특징이 있으며 대부분은 매우 복잡합니다.

  • C(1972), 고(2009): 이러한 시스템은 일반 유형을 지원하지 않으면 전혀 강력하지 않습니다. "정수 목록", "문자열 목록" 등을 의미하는 MyList 유형을 정의하는 것은 불가능합니다. 대신 '지정되지 않은 값 목록'을 작성해야 합니다. 프로그래머는 목록에서 문자열을 검색할 때마다 "이것은 문자열 목록입니다"라고 수동으로 보고해야 하며 이로 인해 실행 오류가 발생할 수 있습니다.
  • 자바(1995), C#(2000): 두 언어 모두 일반 유형을 지원하므로 MyList라고 말할 수 있습니다. 컴파일러가 알고 있고 유형 규칙을 적용할 수 있는 문자열 목록을 가져옵니다. 목록의 요소는 문자열 유형이며 컴파일러는 평소와 같이 컴파일 규칙을 강제하므로 런타임 오류가 발생할 가능성이 줄어듭니다.
  • 하스켈(1990), 러스트(2010), 스위프트(2014): 이러한 모든 언어에는 일반 유형, 대수 데이터 유형(ADT), 유형 클래스 또는 이와 유사한 것(각각 클래스 유형, 특성 및 프로토콜)을 포함한 여러 가지 고급 기능이 있습니다. Rust와 Swift는 Haskell보다 더 인기가 높으며 대규모 조직(각각 Mozilla와 Apple)에서 홍보합니다.
  • 아그다(2007), 이드리스(2011): 이러한 언어는 종속 유형을 지원하므로 "y가 x보다 큰 두 정수 x와 y를 취하는 함수"와 같은 유형을 만들 수 있습니다. 컴파일 중에는 "y가 x보다 큼" 제약 조건도 강제 적용됩니다. 실행되면 무슨 일이 일어나더라도 y는 x보다 작거나 같을 수 없습니다. 시스템의 매우 미묘하지만 중요한 속성을 이러한 언어에서 정적으로 확인할 수 있습니다. 이를 연구하는 프로그래머는 거의 없지만 이러한 언어는 그들 사이에서 큰 열정을 불러일으킵니다.

특히 단순히 언어가 존재한다는 사실보다는 언어의 인기로 측정할 때 더욱 강력한 타이핑 시스템을 향한 분명한 움직임이 있습니다. 주목할만한 예외는 Go입니다. 이는 많은 정적 언어 지지자들이 Go를 한 단계 뒤처진 것으로 간주하는 이유를 설명합니다.


그룹 2(Java 및 C#)는 성숙하고 널리 사용되는 주류 언어입니다.


그룹 3은 Mozilla(Rust)와 Apple(Swift)의 전폭적인 지원을 받아 주류 진입을 앞두고 있습니다.


그룹 4(Idris 및 Agda)는 주류와는 거리가 멀지만 시간이 지나면 바뀔 수 있습니다. 3군 언어는 10년 전만 해도 주류와는 거리가 멀었다.

이 기사에는 동적 타이핑을 나쁘게, Lisp를 유형 없는 언어, C를 강력한 유형의 언어라고 부르지 않기 위해 타이핑에 대해 알아야 할 최소한의 사항이 포함되어 있습니다.

정식 버전은 상세 설명모든 유형의 타이핑, 코드 예제, 인기 있는 프로그래밍 언어에 대한 링크 및 예시 사진이 포함되어 있습니다.

먼저 기사의 짧은 버전을 읽어보고 원하시면 전체 버전을 읽어 보시기 바랍니다.

짧은 버전

타이핑에 따라 프로그래밍 언어는 일반적으로 타입이 있는 언어와 타입이 없는(타입이 없는) 두 개의 큰 캠프로 나뉩니다. 첫 번째에는 C, Python, Scala, PHP 및 Lua가 포함되고 두 번째에는 어셈블리 언어인 Forth 및 Brainfuck이 포함됩니다.

"무타입 타이핑"은 본질적으로 플러그처럼 단순하기 때문에 더 이상 다른 유형으로 분류되지 않습니다. 그러나 입력된 언어는 몇 가지 더 겹치는 범주로 나뉩니다.

  • 정적/동적 타이핑. 정적은 변수와 함수의 최종 유형이 컴파일 타임에 설정된다는 사실로 정의됩니다. 저것들. 컴파일러는 이미 어떤 유형이 어디에 있는지 100% 확신합니다. 동적 타이핑에서는 프로그램 실행 중에 모든 유형이 발견됩니다.

    예:
    정적: C, 자바, C#;
    동적: Python, JavaScript, Ruby.

  • 강한/약한 타이핑(때때로 강한/느슨한 타이핑이라고도 함). 강력한 타이핑은 언어에서 표현식 혼합을 허용하지 않는다는 점에서 구별됩니다. 다양한 방식자동 암시적 변환을 수행하지 않습니다. 예를 들어 문자열에서 집합을 뺄 수 없습니다. 약한 유형의 언어는 정밀도 손실이 발생하거나 변환이 모호하더라도 자동으로 많은 암시적 변환을 수행합니다.

    예:
    강력함: Java, Python, Haskell, Lisp;
    약함: C, JavaScript, Visual Basic, PHP.

  • 명시적/암시적 유형 지정. 명시적 유형 언어는 새 변수/함수/해당 인수의 유형을 명시적으로 지정해야 한다는 점에서 다릅니다. 따라서 암시적 타이핑을 사용하는 언어는 이 작업을 컴파일러/인터프리터에게 맡깁니다.

    예:
    명시적: C++, D, C#
    암시적: PHP, Lua, JavaScript

또한 이러한 모든 범주가 ​​중복된다는 점에 유의해야 합니다. 예를 들어 C 언어에는 정적 약한 명시적 유형 지정이 있으며 파이썬 언어- 동적 강함 암시적.

그러나 정적 타이핑과 동적 타이핑이 동시에 가능한 언어는 없습니다. 앞을 내다 보면 내가 여기에 누워 있다고 말할 것입니다. 실제로 존재하지만 이에 대해서는 나중에 자세히 설명합니다.

상세 버전

짧은 버전이 충분하지 않다면 괜찮습니다. 내가 자세하게 쓴 게 괜히 있는 게 아니지? 가장 중요한 것은 모든 유용하고 흥미로운 정보를 짧은 버전에 넣는 것이 불가능했으며 자세한 정보는 모든 사람이 긴장하지 않고 읽기에는 너무 길다는 것입니다.

무형식 타이핑

유형이 없는 프로그래밍 언어에서 모든 엔터티는 단순히 다양한 길이의 비트 시퀀스로 간주됩니다.

유형 없는 타이핑은 일반적으로 저수준 언어(어셈블리 언어, Forth)와 난해한 언어(Brainfuck, HQ9, Piet)에 내재되어 있습니다. 그러나 단점과 함께 몇 가지 장점도 있습니다.

장점
  • 매우 낮은 수준에서 작성할 수 있으며 컴파일러/인터프리터는 유형 검사를 방해하지 않습니다. 모든 유형의 데이터에 대해 자유롭게 작업을 수행할 수 있습니다.
  • 결과 코드는 일반적으로 더 효율적입니다.
  • 지침의 투명성. 언어를 안다면 일반적으로 이 코드나 해당 코드가 무엇인지 의심할 여지가 없습니다.
결함
  • 복잡성. 목록, 문자열, 구조체 등 복잡한 값을 표현해야 하는 경우가 많습니다. 불편을 끼칠 수 있습니다.
  • 수표가 부족합니다. 기호에서 배열에 대한 포인터를 빼는 것과 같은 의미 없는 작업은 완전히 정상적인 것으로 간주되며 미묘한 오류가 발생합니다.
  • 낮은 수준의 추상화. 복잡한 데이터 유형으로 작업하는 것은 숫자로 작업하는 것과 다르지 않으며, 이는 물론 많은 어려움을 야기합니다.
강력한 무형식 타이핑?

예, 존재합니다. 예를 들어, 어셈블리 언어(x86/x86-64 아키텍처의 경우 다른 것은 모르겠습니다)에서 rax 레지스터(64비트)의 데이터를 cx 레지스터(16비트)로 로드하려고 하면 프로그램을 어셈블할 수 없습니다. .

mov cx, eax ; 조립시간 오류

그럼 어셈블러에는 여전히 타이핑이 있다는 것이 밝혀졌나요? 나는 이러한 수표만으로는 충분하지 않다고 생각합니다. 물론 귀하의 의견은 귀하에게만 달려 있습니다.

정적 및 동적 타이핑

정적 유형 지정과 동적 유형 지정을 구별하는 가장 중요한 점은 모든 유형 검사가 런타임이 아닌 컴파일 타임에 수행된다는 것입니다.

어떤 사람들은 정적 타이핑이 너무 제한적이라고 생각할 수도 있습니다(사실 그렇습니다. 그러나 이것은 일부 기술의 도움으로 오랫동안 제거되었습니다). 어떤 사람들은 동적 타이핑 언어가 불장난을 하고 있다고 말하는데, 어떤 특징이 이 언어를 돋보이게 할까요? 두 종 모두 실제로 존재할 가능성이 있습니까? 그렇지 않다면 정적 및 동적으로 유형이 지정되는 언어가 왜 그렇게 많습니까?

그것을 알아 봅시다.

정적 유형 지정의 이점
  • 유형 검사는 컴파일 단계에서 한 번만 발생합니다. 즉, 숫자를 문자열로 나누려고 하는지(그리고 오류가 발생하는지 변환을 수행하는지) 끊임없이 알아낼 필요가 없습니다.
  • 실행 속도. 이전 시점에서 정적으로 유형이 지정된 언어는 거의 항상 동적으로 유형이 지정된 언어보다 빠르다는 것이 분명합니다.
  • 일부 추가 조건에서는 컴파일 단계에서 이미 잠재적인 오류를 감지할 수 있습니다.
동적 타이핑의 이점
  • 범용 컬렉션 생성 용이성 - 모든 것과 모든 사람의 힙(이러한 필요성은 거의 발생하지 않지만 동적 타이핑이 발생하면 도움이 될 것입니다).
  • 일반화된 알고리즘 설명의 편의성(예: 정수 목록뿐만 아니라 실수 목록, 심지어 문자열 목록에서도 작동하는 배열 정렬)
  • 배우기 쉬움 - 동적 유형의 언어는 일반적으로 프로그래밍을 시작하는 데 매우 좋습니다.

일반화된 프로그래밍

좋아요, 동적 타이핑에 대한 가장 중요한 주장은 일반 알고리즘을 설명하는 편리함입니다. 문제를 상상해 봅시다. 정수 배열, 실수 배열, 문자 배열 등 여러 배열(또는 목록)을 검색하는 함수가 필요합니다.

어떻게 해결할까요? 3가지 다른 언어로 문제를 해결해 보겠습니다. 하나는 동적 타이핑이고 두 개는 정적 타이핑입니다.

가장 간단한 검색 알고리즘 중 하나인 무차별 대입을 사용하겠습니다. 이 함수는 검색 중인 요소, 배열(또는 목록) 자체를 수신하고 요소의 인덱스를 반환하거나 요소를 찾을 수 없는 경우 - (-1)을 반환합니다.

동적 솔루션(Python):

Def find(required_element, list): for (index, element) in enumerate(list): if element == 필수_element: return index return (-1)

보시다시피 모든 것이 간단하며 목록에 숫자, 목록 또는 기타 배열이 포함될 수 있다는 사실에는 문제가 없습니다. 매우 좋은. 더 나아가서 C에서 동일한 문제를 해결해 보세요!

정적 용액(C):

Unsigned int find_int(int 필수_요소, int 배열, unsigned int 크기) ( for (unsigned int i = 0; i< size; ++i) if (required_element == array[i]) return i; return (-1); } unsigned int find_float(float required_element, float array, unsigned int size) { for (unsigned int i = 0; i < size; ++i) if (required_element == array[i]) return i; return (-1); } unsigned int find_char(char required_element, char array, unsigned int size) { for (unsigned int i = 0; i < size; ++i) if (required_element == array[i]) return i; return (-1); }

글쎄, 각 함수는 개별적으로 Python 버전과 유사하지만 왜 세 개가 있습니까? 정적 프로그래밍이 정말 손실되었나요?

예, 아니오. 여러 가지 프로그래밍 기술이 있는데, 그 중 하나를 이제 살펴보겠습니다. 이를 일반 프로그래밍이라고 하며 C++ 언어는 이를 매우 잘 지원합니다. 새 버전을 살펴보겠습니다.

정적 솔루션(일반 프로그래밍, C++):

주형 unsigned int find(T 필수 요소, std::벡터 배열) ( for (unsigned int i = 0; i< array.size(); ++i) if (required_element == array[i]) return i; return (-1); }

괜찮은! Python 버전보다 훨씬 더 복잡해 보이지 않으며 많은 작성이 필요하지 않습니다. 또한 문제를 해결하는 데 필요한 3개 어레이뿐만 아니라 모든 어레이에 대한 구현을 얻었습니다!

이 버전은 우리에게 꼭 필요한 것 같습니다. 정적 타이핑의 장점과 동적 타이핑의 장점 중 일부를 모두 얻습니다.

이것이 가능하다는 것은 좋은 일이지만, 더 좋을 수도 있습니다. 첫째, 일반화된 프로그래밍이 더 편리하고 아름다울 수 있습니다(예: Haskell 언어 사용). 둘째, 일반화된 프로그래밍 외에도 다형성(결과가 더 나쁠 수 있음), 함수 오버로딩(유사) 또는 매크로를 사용할 수도 있습니다.

역학의 정적

또한 많은 정적 언어가 동적 타이핑을 허용한다는 점도 언급해야 합니다. 예를 들면 다음과 같습니다.

  • C#은 동적 의사 유형을 지원합니다.
  • F#은 동적 유형 지정 모방을 구현할 수 있는 ? 연산자 형식의 설탕 구문을 지원합니다.
  • Haskell - 동적 타이핑은 Data.Dynamic 모듈에서 제공됩니다.
  • Delphi - 특별한 Variant 유형을 통해.

또한 일부 동적 유형 지정 언어를 사용하면 정적 유형 지정을 활용할 수 있습니다.

  • Common Lisp - 유형 선언.
  • Perl - 버전 5.6 이후로는 상당히 제한적입니다.

강한 타이핑과 약한 타이핑

강력한 유형의 언어는 서로 다른 유형의 엔터티가 표현식에 혼합되는 것을 허용하지 않으며 어떤 작업도 수행하지 않습니다. 자동 전환. "강력한 형식의 언어"라고도 합니다. 이에 대한 영어 용어는 강한 타이핑입니다.

반대로 약한 유형의 언어는 프로그래머가 하나의 표현식에 다양한 유형을 혼합하도록 권장하며 컴파일러 자체는 모든 것을 단일 유형으로 줄입니다. "느슨한 형식의 언어"라고도 합니다. 이에 대한 영어 용어는 약한 타이핑입니다.

약한 타이핑은 종종 완전히 잘못된 동적 타이핑과 혼동됩니다. 동적 유형 언어는 약한 유형 또는 강력한 유형일 수 있습니다.

그러나 엄격한 타이핑을 중요시하는 사람은 거의 없습니다. 언어가 정적으로 입력되면 많은 것을 잡을 수 있다고 종종 언급됩니다. 잠재적인 오류컴파일할 때. 그들은 당신에게 거짓말을 하고 있습니다!

언어에는 강력한 타이핑도 있어야 합니다. 실제로 컴파일러가 오류를 보고하는 대신 단순히 숫자에 문자열을 추가하거나 더 나쁜 경우 한 배열에서 다른 문자열을 뺀다면 유형의 모든 "검사"가 컴파일 시에 수행된다는 것이 우리에게 무슨 소용이 있습니까? 단계? 맞습니다. 약한 정적 타이핑은 강력한 동적 타이핑보다 더 나쁩니다! (뭐, 그건 내 생각이다)

그렇다면 약한 타이핑에는 전혀 이점이 없습니까? 이렇게 보일 수도 있지만, 내가 강한 타이핑을 열렬히 지지한다는 사실에도 불구하고 약한 타이핑에도 장점이 있다는 점에는 동의해야 합니다.

어떤 것을 알고 싶으십니까?

강력한 타이핑의 이점
  • 신뢰성 - 잘못된 동작 대신 예외 또는 컴파일 오류가 발생합니다.
  • 속도 - 꽤 비용이 많이 들 수 있는 숨겨진 변환 대신 강력한 타이핑을 사용하여 명시적으로 작성해야 하며, 이로 인해 프로그래머는 최소한 이 코드 조각이 느릴 수 있다는 것을 알 수 있습니다.
  • 프로그램 작동 방식 이해 - 다시 말하지만, 프로그래머는 암시적 유형 캐스팅 대신 모든 것을 직접 작성합니다. 즉, 문자열과 숫자를 비교하는 일은 마술이 아니라 저절로 발생하지 않는다는 것을 대략적으로 이해합니다.
  • 확실성 - 손으로 변환을 작성할 때 무엇을 변환하고 무엇으로 변환하는지 정확히 알 수 있습니다. 또한 이러한 변환으로 인해 정밀도가 떨어지고 잘못된 결과가 발생할 수 있다는 점도 항상 알고 있어야 합니다.
약한 타이핑의 이점
  • 혼합 표현식 사용의 편리성(예: 정수와 실수)
  • 타이핑을 추상화하고 작업에 집중합니다.
  • 항목의 간결함.

좋아요, 우리는 그것을 알아냈습니다. 약한 타이핑에도 장점이 있다는 것이 밝혀졌습니다! 약한 타이핑의 장점을 강한 타이핑으로 전환할 수 있는 방법이 있나요?

두 개도 있다는 것이 밝혀졌습니다.

명확한 상황에서 데이터 손실 없이 암시적 유형 캐스팅

와... 꽤 긴 말이군요. "제한된 암시적 변환"으로 더 줄여보겠습니다. 그러면 명확한 상황과 데이터 손실은 무엇을 의미합니까?

모호하지 않은 상황은 본질이 즉각적으로 명확한 변형이나 작업입니다. 예를 들어 두 개의 숫자를 추가하는 것은 명확한 상황입니다. 그러나 숫자를 배열로 변환하는 것은 그렇지 않습니다. 아마도 한 요소의 배열이 생성될 것입니다. 아마도 이러한 길이의 배열은 기본적으로 요소로 채워지고 숫자는 문자열로 변환된 다음 배열로 변환될 것입니다. 문자).

데이터를 잃는 것이 훨씬 더 쉽습니다. 실수 3.5를 정수로 변환하면 데이터의 일부가 손실됩니다(사실 이 연산도 모호합니다. 반올림은 어떻게 수행됩니까? 위로? 아래로? 분수 부분을 버리나요?).

모호한 상황에서의 변환과 데이터 손실이 있는 변환은 매우 나쁩니다. 프로그래밍에서 이보다 더 나쁜 것은 없습니다.

내 말을 믿을 수 없다면 PL/I 언어를 공부하거나 사양을 찾아보세요. 모든 데이터 유형 간 변환에 대한 규칙이 있습니다! 이건 그냥 지옥이야!

좋아요, 제한된 암시적 변환에 대해 기억해 봅시다. 그런 언어도 있나요? 예, 예를 들어 Pascal에서는 정수를 실수로 변환할 수 있지만 그 반대는 불가능합니다. C#, Groovy 및 Common Lisp에도 유사한 메커니즘이 있습니다.

좋아, 나는 강력한 언어에서 약한 타이핑의 몇 가지 장점을 얻을 수 있는 방법이 아직 있다고 말했습니다. 그리고 그렇습니다. 그것은 존재하며 생성자 다형성이라고 불립니다.

훌륭한 하스켈 언어의 예를 들어 설명하겠습니다.

다형성 생성자는 숫자 리터럴을 사용할 때 안전한 암시적 변환이 가장 자주 필요하다는 관찰에서 탄생했습니다.

예를 들어, pi + 1 표현식에서 pi + 1.0 또는 pi + float(1) 을 쓰고 싶지 않을 것입니다. 나는 단지 pi + 1을 쓰고 싶습니다!

그리고 이것은 하스켈에서 이루어집니다. 리터럴 1이 없기 때문입니다. 특정 유형. 그것은 전체도 아니고 실제도 아니며 복잡하지도 않습니다. 그것은 단지 숫자일 뿐입니다!

결과적으로 간단한 함수 sum x y 를 작성하고 x에서 y까지의 모든 숫자(1씩 증분)를 곱하면 한 번에 여러 버전을 얻게 됩니다. 정수의 합, 실수의 합, 유리수의 합, 복소수의 합 그리고 여러분이 직접 정의한 모든 숫자 유형에 대한 합계도 계산할 수 있습니다.

물론 이 기술은 숫자 리터럴이 포함된 혼합 표현식을 사용할 때만 비용을 절감하며 이는 빙산의 일각에 불과합니다.

따라서 가장 좋은 해결책은 강한 타이핑과 약한 타이핑 사이의 경계에서 균형을 맞추는 것이라고 말할 수 있습니다. 하지만 아직 완벽한 균형을 이루는 언어는 없기 때문에 약한 유형의 언어(예: C, JavaScript, Lua, PHP)보다는 강력한 유형의 언어(예: Haskell, Java, C#, Python)에 더 의지합니다.

명시적 및 암시적 입력

명시적으로 유형이 지정된 언어에서는 프로그래머가 선언하는 모든 변수 및 함수의 유형을 지정해야 합니다. 이에 대한 영어 용어는 명시적 타이핑입니다.

반면에 암시적으로 유형이 지정된 언어는 유형을 잊어버리고 유형 추론 작업을 컴파일러나 해석기에 맡기도록 권장합니다. 이에 대한 영어 용어는 암시적 타이핑입니다.

처음에는 암시적 유형 지정이 동적과 동일하고 명시적 유형 지정이 정적과 동일하다고 생각할 수 있지만 나중에는 그렇지 않다는 것을 알게 됩니다.

각 유형마다 장점이 있나요? 그리고 또 이들의 조합이 있나요? 두 가지 방법을 모두 지원하는 언어가 있나요?

명시적 입력의 이점
  • 각 함수에 서명(예: int add(int, int))이 있으면 함수가 수행하는 작업을 쉽게 확인할 수 있습니다.
  • 프로그래머는 특정 변수에 어떤 유형의 값을 저장할 수 있는지 즉시 기록하므로 이를 기억할 필요가 없습니다.
암시적 유형 지정의 이점
  • 약식 표기법 - def add(x, y) 는 int add(int x, int y) 보다 분명히 짧습니다.
  • 변화에 대한 저항. 예를 들어, 함수에서 임시 변수가 입력 인수와 동일한 유형인 경우 명시적으로 유형이 지정된 언어에서는 입력 인수의 유형을 변경할 때 임시 변수의 유형도 변경해야 합니다.

좋아요, 두 접근 방식 모두 장단점이 있다는 것은 분명합니다(누가 또 예상했겠습니까?). 따라서 이 두 접근 방식을 결합하는 방법을 찾아보겠습니다.

선택에 의한 명시적 입력

기본적으로 암시적 타이핑이 가능한 언어와 필요한 경우 값 유형을 지정할 수 있는 기능이 있습니다. 번역기는 실제 유형의 표현식을 자동으로 출력합니다. 이러한 언어 중 하나는 Haskell입니다. 명확성을 위해 간단한 예를 들어 보겠습니다.

명시적 유형 지정 없이 add (x, y) = x + y -- 명시적 유형 지정 add:: (Integer, Integer) -> Integer add (x, y) = x + y

참고: 저는 의도적으로 커링되지 않은 함수를 사용했으며 더 일반적인 add::(Num a) -> a -> a -> a 대신 의도적으로 개인 서명을 작성했습니다. 하스켈 구문을 설명하지 않고 아이디어를 보여주고 싶었습니다.

흠. 보시다시피 매우 훌륭하고 짧습니다. 함수를 작성하려면 공백을 포함하여 한 줄에 18자만 입력하면 됩니다!

그러나 자동 유형 추론은 상당히 복잡한 일이며 Haskell과 같은 멋진 언어에서도 때때로 실패합니다. (예는 단형성 제약 조건입니다)

기본적으로 명시적 타이핑이 있고 필요한 경우 암시적 타이핑이 있는 언어가 있나요? 범죄자
확신하는.

선택에 의한 암시적 입력

C++11(이전에는 C++0x라고 함)이라는 새로운 C++ 언어 표준에는 컴파일러가 컨텍스트를 기반으로 형식을 추론할 수 있도록 하는 auto 키워드가 도입되었습니다.

비교해 보겠습니다. // unsigned 유형을 수동으로 지정 int a = 5; 부호 없는 int b = a + 3; // unsigned int a = 5 유형의 자동 출력; 자동 b = a + 3;

나쁘지 않다. 하지만 기록은 크게 줄지 않았다. 반복자가 포함된 예를 살펴보겠습니다(이해하지 못하더라도 걱정하지 마세요. 가장 중요한 점은 자동 출력 덕분에 녹음이 크게 줄어든다는 것입니다).

// std::Vector 유형을 수동으로 지정 vec = 무작위벡터(30); for (std::Vector::const_iterator it = vec.cbegin(); ...) ( ... ) // 자동 유형 추론 auto vec = randomVector (서른); for (auto it = vec.cbegin(); ...) ( ... )

우와! 이것이 약어입니다. 좋아, 하지만 반환 유형이 인수 유형에 따라 달라지는 Haskell과 같은 작업을 수행하는 것이 가능합니까?

그리고 대답은 '예'입니다. auto와 결합된 decltype 키워드 덕분입니다.

// 수동 유형 int Divide(int x, int y) ( ... ) // 자동 유형 추론 auto Divide(int x, int y) -> decltype(x / y) ( ... )

이러한 형태의 표기법은 별로 좋아 보이지 않을 수 있지만 일반 프로그래밍(템플릿/제네릭)과 결합하면 암시적 유형 지정 또는 자동 유형 추론이 놀라운 효과를 발휘합니다.

이 분류에 따른 일부 프로그래밍 언어

나는 인기있는 언어의 작은 목록을 제공하고 "타이핑"의 각 범주로 어떻게 나뉘는지 쓸 것입니다.

JavaScript - 동적 / 약함 / 암시적 Ruby - 동적 / 강력 / 암시적 Python - 동적 / 강력 / 암시적 Java - 정적 / 강력 / 명시적 PHP - 동적 / 약 / 암시적 C - 정적 / 약 / 명시적 C++ - 정적 / 준강함 / 명시적 Perl - 동적 / 약함 / 암시적 Objective-C - 정적 / 약함 / 명시적 C# - 정적 / 강력 / 명시적 Haskell - 정적 / 강력 / 암시적 Common Lisp - 동적 / 강력 / 암시적

특히 CL, PHP 및 Obj-C에서 실수를 저질렀을 수도 있습니다. 일부 언어에 대해 다른 의견이 있으면 의견을 적어주세요.

입력 - 정보 개체에 유형을 할당합니다.

가장 일반적인 기본 데이터 유형은 다음과 같습니다.

  • 숫자
  • 상징적
  • 논리적

데이터 유형 시스템의 주요 기능:

  • 보안
    각 작업은 의도된 유형의 인수를 정확히 수신하는지 확인하기 위해 검사됩니다.
  • 최적화
    유형에 따라 효율적인 저장 방법과 이를 처리하는 알고리즘이 선택됩니다.
  • 선적 서류 비치
    프로그래머의 의도가 강조됩니다.
  • 추출
    상위 수준 데이터 유형을 사용하면 프로그래머는 값을 비트 모음이 아닌 상위 수준 엔터티로 생각할 수 있습니다.

분류

프로그래밍 언어 유형에는 여러 가지 분류가 있지만 주요 분류는 3가지뿐입니다.

정적/동적 타이핑

정적 - 유형 일관성의 할당 및 확인은 컴파일 단계에서 수행됩니다. 데이터 유형은 특정 값이 아닌 변수와 연결됩니다. 정적 타이핑을 사용하면 컴파일 단계에서 거의 사용되지 않는 프로그램 로직 분기에서 발생한 타이핑 오류를 찾을 수 있습니다.

동적 타이핑은 정적 타이핑의 반대입니다. 동적 타이핑에서는 프로그램 실행 중에 모든 유형이 발견됩니다.

동적 타이핑을 사용하면 타이핑 오류가 발생할 가능성이 높아지더라도 보다 유연한 소프트웨어를 만들 수 있습니다. 단위 테스트는 개발에서 특히 중요해집니다. 소프트웨어동적 타이핑을 사용하는 프로그래밍 언어에서는 거의 사용되지 않는 프로그램 로직 분기에서 발생하는 타이핑 오류를 찾는 유일한 방법이기 때문입니다.

동적 타이핑

Var LuckyNumber = 777; var siteName = "Tyapk"; // 숫자를 가정하고 문자열을 씁니다. varwrongNumber = "999";

정적 타이핑

LuckyNumber: 숫자 = 777; let siteName: string = "Tyapk"; // 오류가 발생합니다. letwrongNumber: number = "999";

  • 정적: Java, C#, TypeScript.
  • 동적: Python, Ruby, JavaScript.

명시적/암시적 유형 지정.

명시적 유형 언어는 새 변수/함수/해당 인수의 유형을 명시적으로 지정해야 한다는 점에서 다릅니다. 따라서 암시적 타이핑을 사용하는 언어는 이 작업을 컴파일러/인터프리터에게 맡깁니다. 명시적 타이핑은 암시적 타이핑의 반대입니다.

명시적 유형 지정에는 사용된 각 변수에 대한 명시적 유형 선언이 필요합니다. 이러한 유형의 타이핑은 정적 타이핑의 특별한 경우입니다. 각 변수의 유형은 컴파일 타임에 결정됩니다.

암시적 타이핑

stringVar = "777" + 99; // "77799"를 얻습니다.

명시적 타이핑(JS와 유사한 가상 언어)

잘못StringVar = "777" + 99; // 오류가 발생합니다. let stringVar = "777" + String(99); // "77799"를 얻습니다.

강력/강하지 않은 타이핑

강한/약한 타이핑이라고도 합니다. 엄격한 유형 지정을 사용하면 유형이 "한 번에" 할당되고, 엄격하지 않은 유형 지정을 사용하면 프로그램 실행 중에 유형이 변경될 수 있습니다.

강력한 유형의 언어는 변수의 데이터 유형 변경을 금지하고 명시적인 데이터 유형 변환만 허용합니다. 강력한 유형 지정은 언어가 표현식에 다양한 유형을 혼합하는 것을 허용하지 않고 자동 암시적 변환을 수행하지 않는다는 점에서 구별됩니다. 예를 들어 문자열에서 숫자를 뺄 수 없습니다. 약한 유형의 언어는 정밀도 손실이 발생하거나 변환이 모호하더라도 자동으로 많은 암시적 변환을 수행합니다.

강력한 타이핑(JS와 유사한 가상 언어)

잘못번호 = 777; 잘못된 번호 = 잘못된 번호 + "99"; // 숫자 변수에 문자열이 추가되었다는 오류가 발생합니다.wrongNumber let trueNumber = 777 + Number("99"); // 876을 얻습니다.

엄격하지 않은 유형 지정(js의 경우와 동일)

잘못번호 = 777; 잘못된 번호 = 잘못된 번호 + "99"; // 문자열 "77799"를 얻었습니다.

  • 엄격함: Java, Python, Haskell, Lisp.
  • 엄격하지 않음: C, JavaScript, Visual Basic, PHP.
공유하다