재사용할 작업에 맞춰 이름 있는 함수, 람다, 제네릭 함수를 골라 보세요. 예제에서 각 형태의 입력 타입과 반환 타입이 어떻게 연결되는지 확인합니다.
첫 프로그램을 따라 토파즈를 설치하세요. 함수 선언이 낯설다면 값과 함수부터 시작하세요.
세 가지 함수 형태 실행하기
작업 폴더에 다음 코드를 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검사가 성공하면 종료 코드는 0입니다. 프로그램은 다음 세 줄을 출력합니다.
5
Topaz
레이블 { 텍스트: docs }함수를 인수로 전달하기
두번적용은 정수와 함수를 받아 정수를 반환합니다. 단계 매개변수의 타입이 (int) -> int이므로 전달할 함수는 int를 받고 int를 반환해야 합니다.
증가는 =>로 쓴 이름 없는 함수인 람다입니다. 입력에 1을 더합니다. 두번적용(3, 증가)는 증가(증가(3))을 계산하므로 결과가 5입니다.
함수 본문의 마지막 표현식이 반환값이 됩니다. 그 전에 함수를 끝내려면 return을 사용하세요. 람다는 값으로 저장하거나 전달할 수 있고 주변 바인딩을 캡처할 수 있습니다. 호출 사이에 값을 유지하는 카운터는 바인딩·스코프·클로저에서 확인하세요.
여러 타입에 같은 함수 사용하기
첫값또는<자료형>은 배열의 첫 원소를 반환하며, 배열이 비어 있으면 대체값을 반환합니다. 자료형은 타입 매개변수입니다. 배열 원소, 대체값, 반환값이 같은 타입이라는 관계를 나타냅니다.
예제는 문자열 배열을 전달하므로 토파즈가 자료형 = string으로 추론하고 "Topaz"를 반환합니다. 배열을 []로 바꾸면 "none"을 반환합니다. 이때도 대체값을 보고 타입을 string으로 정할 수 있습니다.
이를 1계층 다형성이라고 합니다. 타입 매개변수는 이름 있는 함수에 선언하며 각 호출에서 타입을 정합니다. 람다의 매개변수는 주변 문맥에서 구체적인 타입을 받습니다. 첫값또는처럼 같은 알고리즘을 여러 타입에 적용할 때 이름 있는 제네릭 함수를 사용하세요.
제약 조건으로 필요한 연산 지정하기
값표시는 Show.show를 호출해 인수를 문자열로 바꿉니다. Show는 정적 프로토콜입니다. 연산 묶음에 이름을 붙이고, 컴파일러가 그 구현 여부를 검사합니다. 제약 조건 자료형: Show는 인수의 타입이 이 프로토콜을 구현하도록 요구합니다.
레이블 derives Show는 레이블에 그 구현을 제공합니다. 선언한 타입에 구현을 연결하므로 명목적 준수라고 합니다. 기본 타입, 구조적 레코드, 함수, 컬렉션에 비슷한 연산이 있어도 이 준수 관계가 자동으로 생기지는 않습니다.
값표시가 Show.show를 호출하는 동안에는 자료형: Show를 유지하세요. 제약 조건을 지우면 그 호출이 정적 오류가 됩니다. 두 프로토콜을 사용하는 본문에는 자료형: Show + JSON을 쓰세요. 두 조건을 모두 충족해야 하며 표기 순서는 상관없습니다.
함수 형태 고르기
| 필요한 것 | 사용할 형태 |
|---|---|
| 입력·반환 타입이 정해진 재사용 작업 | 구체적인 타입을 쓰는 이름 있는 함수 |
| 값으로 전달하거나 저장할 작업 | 람다 |
| 여러 타입의 입력·반환값을 연결하는 같은 알고리즘 | 이름 있는 제네릭 함수 |
| 제네릭 본문에서 프로토콜 연산 사용 | 필요한 제약 조건을 선언한 이름 있는 제네릭 함수 |
기본값과 가변 인수 사용하기
매개변수는 불변입니다. 인수를 생략하면 기본값을 평가합니다. 기본값은 리터럴이나 const 표현식이어야 하며 다른 매개변수를 참조할 수 없습니다.
인수 개수가 달라지는 함수에서는 마지막 매개변수를 ...args: T로 쓰세요. 함수 안에서 args의 타입은 Array<T>입니다.
| 호출 규칙 | 동작 |
|---|---|
| 함수와 인수 평가 | 호출 대상을 한 번 평가한 뒤 위치 인수와 스프레드 인수를 왼쪽부터 평가 |
| 이름 붙인 인수 | 위치 인수와 스프레드 인수 뒤에 배치 |
| 배열 스프레드 | 가변 인수를 채우며 고정 매개변수는 채우지 않음 |
| 명시적 타입 인수 | 선언 순서대로 전부 적고 값 인수·기대 반환 타입과 맞춤 |
제네릭 호출에서는 보통 타입 인수를 추론합니다. 타입 인수를 직접 지정할 수 있는 호출에서도 일부만 적으면 정적 오류입니다.
지원하는 제네릭 선언
함수 자체의 타입 매개변수와 프로토콜 제약 조건은 이름 있는 함수에 선언하세요. 람다는 주변 문맥에서 구체적인 타입을 받으며 자체 타입 매개변수를 선언할 수 없습니다.
- 람다, 함수 타입, 타입 별칭, 명목 선언, 수신자 메서드, 프로토콜 메서드는 제약 조건을 선언할 수 없습니다.
- 제네릭 본문에서는 선언한 제약 조건이 허용하는 연산을 사용할 수 있습니다. 더 강한 제약 조건이 필요한 연산을 쓰면 정적 오류입니다.
- 고위 다형성, 제네릭 람다, 타입 매개변수 기본값, 분산 표기, 동적 프로토콜 디스패치, 임의의 트레이트 제약 조건은 지원하지 않습니다.
입력·반환 타입을 고르려면 타입을 보세요. 함수가 캡처한 값의 변화를 따라가려면 바인딩·스코프·클로저로 이어가세요.