함수는 재사용할 변환 로직에 식별자를 부여하고, 스코프 경계를 오가는 값의 타입을 명시합니다. 구현할 때는 구체적인 명명된 함수로 먼저 시작하세요. 짧은 로직을 값처럼 전달할 때는 람다 표현식을 사용하세요. 단일 타입 관계를 여러 타입에 걸쳐 유지해야 하는 경우에만 타입 매개변수를 도입합니다.
한 프로그램에서 단계별 형태 실행하기
다음 코드를 functions-generics.tpz로 저장합니다.
record 레이블 derives Show {
텍스트: string,
}
function 두번적용(값: int, 단계: (int) -> int) -> int {
단계(단계(값))
}
function 첫값또는<자료형>(값들: Array<자료형>, 대체값: 자료형) -> 자료형 {
match 값들 {
case [] => 대체값
case [첫값, ..] => 첫값
}
}
function 값표시<자료형: Show>(값: 자료형) -> string {
Show.show(값)
}
let 증가: (int) -> int = 값 => 값 + 1
let 첫문자열: string = 첫값또는(["Topaz", "Classic"], "none")
let 표시됨 = 값표시(레이블 { 텍스트: "docs" })
print("{두번적용(3, 증가)}")
print(첫문자열)
print(표시됨)실행하기 전에 검사합니다.
topaz check functions-generics.tpz
topaz run functions-generics.tpz결과는 다음과 같습니다.
5
Topaz
레이블 { 텍스트: docs }이름 있는 함수부터 시작하기
두번적용 함수는 매개변수 두 개와 int 타입 반환값을 선언합니다. 함수 본문의 마지막 표현식이 반환값이 되므로, 이 코드에서는 return 문을 별도로 명시하지 않았습니다. 실행 흐름 중간에 조기 반환을 명확히 표기하고자 할 때는 return을 사용하세요.
단계 매개변수의 타입은 (int) -> int입니다. 호출 측은 int를 받아서 int를 반환하는 함수를 전달해야 합니다. 증가 변수가 이러한 형태의 람다 표현식입니다. 람다는 => 구문으로 작성하며, 일반 값처럼 변수에 할당할 수 있습니다. 또한 클로저 규칙에 따라 주변 바인딩을 캡처합니다.
같은 관계가 반복될 때 타입 매개변수 추가하기
첫값또는<자료형> 함수는 배열 요소를 구성하는 구체적인 타입을 파악할 필요가 없습니다. 문자열·정수·기타 어떤 타입이든 동일하게 다룹니다. 배열 요소·대체값·반환값이 모두 동일한 자료형 타입이라는 관계만 정의합니다. 해당 호출에서는 문자열 배열을 전달하므로 추론 결과는 자료형 = string이 됩니다. 호출 측에서 타입 인자를 직접 지정하지 않아도 됩니다.
이 구조가 1계층 다형성입니다. 명명된 함수 수준에서 제네릭을 정의하며, 각 호출 시점에 구체적인 단일 타입을 결정합니다. 동일한 알고리즘이 여러 타입에 걸쳐 해당 타입 관계를 유지해야 할 때 사용하세요. 단지 구체적 타입 지정을 피할 목적으로 함수를 제네릭으로 설계할 필요는 없습니다.
함수 몸체가 연산을 요구할 때만 제약 추가하기
값표시<자료형: Show> 함수는 본문에서 Show.show를 호출합니다. 이에 따라 함수 본문 내부의 자료형이 정적 Show 프로토콜을 구현한다는 근거가 필요합니다. 레이블 derives Show 선언이 해당 명목 타입에 필요한 준수 규격을 부여합니다. 출력 가능한 구조처럼 보이더라도 구조적 값이나 기본 타입이 프로토콜을 자동으로 구현하지는 않습니다.
타입 제약은 일반 제네릭을 선언한 뒤 필요한 연산 범위만큼 덧붙이는 메커니즘입니다. 함수 본문에서 두 프로토콜의 연산을 모두 사용한다면 제약을 조합하여 요구할 수 있습니다. 명명된 제네릭 함수 선언부에 자료형: Show + JSON 형태로 표기하면 되며, 이때 표기 순서는 상관없습니다.
가장 작은 함수 형태 고르기
| 필요한 항목 | 선택 | 전달되는 스펙 의미 |
|---|---|---|
| 명확한 식별자를 갖는 재사용 로직 | 구체적인 명명된 함수 | 매개변수와 반환값이 공개 시그니처 계약을 형성합니다. |
| 인자로 전달하거나 변수에 저장할 단문 로직 | 람다 표현식 | 주변 문맥을 통해 매개변수의 구체적 타입을 제공합니다. |
| 여러 타입에 걸쳐 단일 관계를 유지하는 알고리즘 | 제약 없는 명명된 제네릭 | 동일한 자료형 타입이 입력 인자와 반환값을 연결합니다. |
| 정적 프로토콜 연산을 호출하는 제네릭 본문 | 제약 조건이 있는 명명된 제네릭 | 본문에서 사용할 연산 범위를 타입 제약에 명시합니다. |
호출, 기본값, 가변 인수
매개변수는 불변입니다. 기본 인자값은 인수를 생략할 때 평가합니다. 기본값에는 리터럴이나 const 표현식만 사용할 수 있으며, 다른 매개변수를 참조할 수는 없습니다. 가변 인자 꼬리의 시그니처 표기는 ...args: T입니다. 이 가변 인자는 매개변수 목록의 마지막에 위치시킵니다. 함수 본문 내부에서는 Array<T> 타입으로 전달받습니다.
호출할 때는 호출 대상을 먼저 한 번 평가합니다. 그다음 위치 인자와 스프레드 인자를 좌측에서 우측 순서로 평가합니다. 명명된 인자는 그 이후에 평가됩니다. 배열 스프레드는 가변 인자 매개변수에만 인수를 보탤 수 있으며, 고정 매개변수를 채우는 용도로는 사용할 수 없습니다.
제네릭 타입은 보통 자동으로 추론됩니다. 타입 인자를 직접 지정하는 호출 형태도 존재하며, 이때는 인자 목록을 빠짐없이 명시해야 합니다. 지정한 타입 인자는 값 인자 및 기대 반환 타입과 일치해야 하며, 일부만 지정하는 표기는 정적 오류입니다.
흔한 실수: 제네릭 람다부터 찾기
익명의 단문 로직은 람다 표현식으로 작성합니다. 구체적인 매개변수 타입은 주변 문맥에서 전달받습니다. 타입 매개변수 선언과 프로토콜 제약 조건은 명명된 함수 선언부에만 둘 수 있습니다. 로직 자체에 재사용 가능한 제네릭 계약이 필요하다면 함수에 식별자를 부여하고 해당 계약을 명시적으로 선언하세요.
정확한 한계
- 제네릭 함수는 1계층으로 제한되며, 고위 함수 값은 지원하지 않습니다.
- 람다·함수 타입·타입 별칭·명목 선언·수신자 메서드·프로토콜 메서드에는 제약을 선언할 수 없습니다.
- 제네릭 람다·타입 매개변수 기본값·분산 표기·동적 프로토콜 디스패치·임의의 트레이트 제약 조건은 지원하지 않습니다.
- 제네릭 함수 본문은 선언된 제약 조건만 보유하므로, 더 강한 연산을 암묵적으로 호출할 수 없습니다.
첫 함수와 람다를 다시 보려면 값과 함수로 돌아가세요. 함수 타입과 제네릭 타입 형태는 타입, 캡처와 수명은 바인딩·스코프·클로저에서 이어집니다.