타입에 대해 더

타입에 대해 더 (More about types)

이 장에서는 타입과 관련된 세부 주제들을 조금 더 깊이 살펴볼게요. 특히 애그리게이트, 오버로딩·자격 표현식, 그리고 문자 타입을 다뤄요.

출처: 타입에 대해 더 문서

본문

애그리게이트 입문 (Aggregates: A primer)

지금까지 애그리게이트에 대해 꽤 많이 이야기했고 여러 예시를 봤어요. 이제 이 기능을 조금 더 자세히 다시 살펴볼게요.

Ada 애그리게이트는 사실상 합성 타입(composite type)의 리터럴 값이에요. 데이터 구조의 초기화를 위한 절차적 코드를 피하는 데 도움을 주는 매우 강력한 표기법이에요.

애그리게이트를 작성할 때의 기본 규칙은 배열이나 레코드의 모든 컴포넌트가 지정되어야 한다는 점이에요. 기본값이 있는 컴포넌트를 포함해서요. 즉 다음 코드는 올바르지 않아요:

package Incorrect is
   type Point is record
      X, Y : Integer := 0;
   end record;

   Origin : Point := (X => 0);
end Incorrect;

표기법을 더 편리하게 만들어주는 몇 가지 지름길이 있어요:

  • 컴포넌트의 기본값을 지정하려면 <> 표기법을 사용할 수 있어요.
  • | 기호를 사용해 여러 컴포넌트에 같은 값을 줄 수 있어요.
  • others 선택을 사용해 아직 지정되지 않은 모든 컴포넌트를 참조할 수 있어요. 단 그 필드들이 모두 같은 타입이어야 해요.
  • 배열에서 연속적인 인덱스 시퀀스를 지정하려면 .. 범위 표기법을 사용할 수 있어요.

다만 이름 붙은 연관(named association)을 사용하기 시작하면, 이후 모든 컴포넌트도 마찬가지로 이름 붙은 연관으로 지정해야 해요.

package Points is
   type Point is record
      X, Y : Integer := 0;
   end record;

   type Point_Array is
     array (Positive range <>) of Point;

   --  use the default values
   Origin   : Point := (X | Y => <>);

   --  likewise, use the defaults
   Origin_2 : Point := (others => <>);

   Points_1 : Point_Array := ((1, 2), (3, 4));
   Points_2 : Point_Array := (1       => (1, 2),
                              2       => (3, 4),
                              3 .. 20 => <>);
end Points;

오버로딩과 자격 표현식 (Overloading and qualified expressions)

Ada에는 이름 오버로딩의 일반적인 개념이 있어요. 열거 타입 절에서 먼저 보았죠. 간단한 예시를 볼게요. Ada에서는 같은 이름이지만 매개변수 타입이 다른 함수를 가질 수 있어요.

package Pkg is
   function F (A : Integer) return Integer;
   function F (A : Character) return Integer;
end Pkg;

이것은 프로그래밍 언어에서 흔한 개념으로 오버로딩 또는 이름 오버로딩이라고 해요.

Ada 오버로딩 기능의 참신한 측면 중 하나는 함수의 반환 타입에 기반해 오버로딩을 해석할 수 있다는 점이에요.

package Pkg is
   type SSID is new Integer;

   function Convert (Self : SSID)
                     return Integer;
   function Convert (Self : SSID)
                     return String;
end Pkg;

with Ada.Text_IO; use Ada.Text_IO;
with Pkg;         use Pkg;

procedure Main is
   S : String := Convert (123_145_299);
   --            ^ Valid, will choose the
   --              proper Convert
begin
   Put_Line (S);
end Main;

주의: 타입에 기반한 오버로드 해석은 Ada에서 함수와 열거 리터럴 모두에 허용돼요. 그래서 같은 이름의 열거 리터럴을 여러 개 가질 수 있어요. 의미상으로 열거 리터럴은 매개변수가 없는 함수처럼 취급돼요.

하지만 때로는 모호성 때문에 오버로드된 이름의 특정 발생이 어떤 선언을 참조하는지 해석할 수 없을 때가 있어요. 이때 자격 표현식(qualified expression) 이 유용해져요.

package Pkg is
   type SSID is new Integer;

   function Convert (Self : SSID)
                     return Integer;
   function Convert (Self : SSID)
                     return String;
   function Convert (Self : Integer)
                     return String;
end Pkg;

with Ada.Text_IO; use Ada.Text_IO;
with Pkg;         use Pkg;

procedure Main is
   S : String := Convert (123_145_299);
   --            ^ Invalid, which convert
   --              should we call?

   S2 : String := Convert (SSID'(123_145_299));
   --                     ^ We specify that the
   --                       type of the
   --                       expression is SSID.

   --  We could also have declared a temporary

   I : SSID := 123_145_299;

   S3 : String := Convert (I);
begin
   Put_Line (S);
end Main;

구문적으로 자격 표현식의 대상은 괄호 안의 어떤 표현식이든, 또는 애그리게이트일 수 있어요:

package Qual_Expr is
   type Point is record
      A, B : Integer;
   end record;

   P : Point := Point'(12, 15);

   A : Integer := Integer'(12);
end Qual_Expr;

이것은 자격 표현식이 프로그래머가 표현식의 타입을 명시적으로 만드는 편리한(때로는 필수적인) 방법임을 보여줘요. 컴파일러를 위해서도, 물론 다른 프로그래머를 위해서도요.

주의: 보기와 느낌이 비슷하지만, 타입 변환과 자격 표현식은 같지 않아요. 자격 표현식은 대상 표현식이 해석될 정확한 타입을 지정하지만, 타입 변환은 대상을 변환하려 시도하고 대상 값을 그렇게 변환할 수 없으면 런타임 오류를 일으켜요. 또한 자격 표현식을 사용해 한 하위 타입에서 다른 하위 타입으로 변환할 수 있는데, 제약이 위반되면 예외가 발생해요.

X : Integer := Natural'(1);

문자 타입 (Character types)

앞서 언급했듯 각 열거 타입은 구별되며 다른 열거 타입과 호환되지 않아요. 하지만 앞서 언급하지 않은 점은 문자 리터럴이 열거 리터럴로 허용된다는 것이에요. 즉 언어의 강한 타입 문자 타입에 더해, 사용자 정의 문자 타입도 허용된다는 뜻이에요:

with Ada.Text_IO; use Ada.Text_IO;

procedure Character_Example is
   type My_Char is ('a', 'b', 'c');
   --  Our custom character type, an
   --  enumeration type with 3 valid values.

   C : Character;
   --  ^ Built-in character type
   --    (it's an enumeration type)

   M : My_Char;
begin
   C := '?';
   --   ^ Character literal
   --     (enumeration literal)

   M := 'a';

   C := 65;
   --   ^ Invalid: 65 is not a
   --     Character value

   C := Character'Val (65);
   --  Assign the character at
   --  position 65 in the
   --  enumeration (which is 'A')

   M := C;
   --   ^ Invalid: C is of type Character,
   --     and M is a My_Char

   M := 'd';
   --   ^ Invalid: 'd' is not a valid
   --     literal for type My_Char
end Character_Example;

이 예시에서는 My_Char 정의에 문자를 사용하고 있어요.

더 알아보기 (Learn more)