데이터와 제어

함수와 제네릭

이름 있는 함수·람다·제네릭을 고르고 입력 타입, 반환 타입, 프로토콜 제약 조건을 읽습니다.

재사용할 작업에 맞춰 이름 있는 함수, 람다, 제네릭 함수를 골라 보세요. 예제에서 각 형태의 입력 타입과 반환 타입이 어떻게 연결되는지 확인합니다.

첫 프로그램을 따라 토파즈를 설치하세요. 함수 선언이 낯설다면 값과 함수부터 시작하세요.

세 가지 함수 형태 실행하기

작업 폴더에 다음 코드를 functions-generics.tpz로 저장하세요.

TOPAZ
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(표시됨)

같은 폴더에서 파일을 검사하고 실행하세요.

BASH
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>입니다.

호출 규칙동작
함수와 인수 평가호출 대상을 한 번 평가한 뒤 위치 인수와 스프레드 인수를 왼쪽부터 평가
이름 붙인 인수위치 인수와 스프레드 인수 뒤에 배치
배열 스프레드가변 인수를 채우며 고정 매개변수는 채우지 않음
명시적 타입 인수선언 순서대로 전부 적고 값 인수·기대 반환 타입과 맞춤

제네릭 호출에서는 보통 타입 인수를 추론합니다. 타입 인수를 직접 지정할 수 있는 호출에서도 일부만 적으면 정적 오류입니다.

지원하는 제네릭 선언

함수 자체의 타입 매개변수와 프로토콜 제약 조건은 이름 있는 함수에 선언하세요. 람다는 주변 문맥에서 구체적인 타입을 받으며 자체 타입 매개변수를 선언할 수 없습니다.

  • 람다, 함수 타입, 타입 별칭, 명목 선언, 수신자 메서드, 프로토콜 메서드는 제약 조건을 선언할 수 없습니다.
  • 제네릭 본문에서는 선언한 제약 조건이 허용하는 연산을 사용할 수 있습니다. 더 강한 제약 조건이 필요한 연산을 쓰면 정적 오류입니다.
  • 고위 다형성, 제네릭 람다, 타입 매개변수 기본값, 분산 표기, 동적 프로토콜 디스패치, 임의의 트레이트 제약 조건은 지원하지 않습니다.

입력·반환 타입을 고르려면 타입을 보세요. 함수가 캡처한 값의 변화를 따라가려면 바인딩·스코프·클로저로 이어가세요.