제네릭(F#)
F#의 클래스, 레코드 및 구분된 공용 구조체 같은 집계 형식과 함수 값, 메서드 및 속성은 제네릭일 수 있습니다. 제네릭 구문에는 적어도 한 개 이상의 형식 매개 변수가 포함되며 이 매개 변수는 일반적으로 제네릭 구문의 사용자가 제공합니다. 제네릭 함수와 형식을 사용하면 다양한 형식에 대해 코드를 반복하지 않고도 각 형식에 사용 가능한 코드를 작성할 수 있습니다. F#에서는 컴파일러의 형식 유추 및 자동 일반화 메커니즘을 통해 코드가 제네릭이라고 암시적으로 유추되는 경우가 많으므로 제네릭 코드를 간단히 만들 수 있습니다.
// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body
// Explicitly generic method.
[ static ] member object-identifer.method-name<type-parameters> parameter-list [ return-type ] =
method-body
// Explicitly generic class, record, interface, structure,
// or discriminated union.
type type-name<type-parameters> type-definition
설명
명시적 제네릭 함수나 형식의 선언은 제네릭이 아닌 함수나 형식의 선언과 매우 비슷하지만, 형식 매개 변수를 지정하고 사용하려면 함수나 형식 이름 뒤에 형식 매개 변수를 꺾쇠 괄호로 묶어야 한다는 점에서 차이가 있습니다.
선언은 대개 제네릭인 것으로 간주됩니다. 함수나 형식을 작성하는 데 사용되는 모든 매개 변수의 형식을 완전히 지정하지 않으면 컴파일러에서 사용자가 작성하는 코드에 포함된 각 매개 변수의 형식, 값 및 변수를 유추합니다. 자세한 내용은 형식 유추(F#)를 참조하십시오. 형식이나 함수의 코드에서 매개 변수의 형식을 별도로 제한하지 않으면 함수나 형식이 제네릭인 것으로 간주됩니다. 이 프로세스를 자동 일반화라고 합니다. 자동 일반화에는 몇 가지 제약이 있습니다. 예를 들어 F# 컴파일러에서 제네릭 구문의 형식을 유추할 수 없으면 값 제한이라는 제한 사항을 가리키는 오류가 컴파일러를 통해 보고됩니다. 이 경우 사용자가 몇 가지 형식 주석을 추가해야 할 수도 있습니다. 자동 일반화 및 값 제한에 대한 자세한 내용과 문제를 해결하기 위해 코드를 변경하는 방법은 자동 일반화(F#)를 참조하십시오.
위 구문에서 type-parameters는 알 수 없는 형식을 나타내는 매개 변수의 목록입니다. 각 매개 변수는 쉼표로 구분되며 각각 작은따옴표로 시작합니다. 경우에 따라서는 해당 형식 매개 변수에 사용 가능한 형식을 추가로 제한하는 제약 조건 절이 포함될 수 있습니다. 다양한 종류의 제약 조건 절 구문 및 제약 조건에 대한 자세한 내용은 제약 조건(F#)을 참조하십시오.
위 구문의 type-definition은 제네릭이 아닌 형식의 형식 정의와 같습니다. 여기에는 클래스 형식에 대한 생성자 매개 변수, 선택적 as 절, 등호, 레코드 필드, inherit 절, 구분된 공용 구조체의 선택 항목, let 및 do 바인딩, 멤버 정의를 비롯하여 제네릭이 아닌 형식 정의에 허용되는 기타 모든 항목이 포함됩니다.
나머지 구문 요소는 제네릭이 아닌 함수 및 형식의 구문 요소와 같습니다. 예를 들어 object-identifier는 포함 개체 자체를 나타내는 식별자입니다.
속성, 필드 및 생성자는 바깥쪽 형식보다 제네릭 수준이 더 높을 수 없습니다. 또한 모듈의 값은 제네릭일 수 없습니다.
암시적 제네릭 구문
F# 컴파일러에서 코드의 형식을 유추할 때는 제네릭일 수 있는 모든 함수가 제네릭이라고 자동으로 간주됩니다. 매개 변수 형식 같이 형식을 명시적으로 지정하면 자동 일반화가 이루어지지 않습니다.
다음 코드 예제에서 makeList나 해당 매개 변수를 어느 것도 제네릭이라고 명시적으로 선언하지 않았음에도 불구하고 이 함수는 제네릭인 것으로 간주됩니다.
let makeList a b =
[a; b]
함수의 시그니처는 'a -> 'a -> 'a list인 것으로 유추됩니다. 이 예제에서 a와 b는 형식이 같은 것으로 유추됩니다. 두 매개 변수가 모두 동일한 목록에 포함되어 있는데 한 목록의 요소는 형식이 모두 동일해야 하기 때문입니다.
형식 주석에 작은따옴표 구문을 사용하여 매개 변수 형식이 제네릭 형식 매개 변수인 것으로 표시하여 함수를 제네릭으로 만들 수도 있습니다. 다음 코드에서는 function1의 매개 변수가 이와 같은 방법에 의해 형식 매개 변수로 선언되었으므로 이 함수도 제네릭인 것으로 간주됩니다.
let function1 (x: 'a) (y: 'a) =
printfn "%A %A" x y
명시적 제네릭 구문
꺾쇠 괄호(< >) 안에 함수의 형식 매개 변수를 명시적으로 선언하여 함수를 제네릭으로 만들 수도 있습니다. 다음 코드에서는 이를 보여 줍니다.
let function2<'T> x y =
printfn "%A, %A" x y
제네릭 구문 사용
제네릭 함수나 메서드를 사용할 때는 형식 인수를 지정하지 않아도 됩니다. 컴파일러에서 형식 유추를 통해 적절한 형식 인수를 유추합니다. 그러나 형식을 유추하기에 모호한 부분이 있으면 형식 인수를 꺾쇠 괄호로 묶어 제공할 수 있습니다. 형식 인수가 여러 개이면 각 인수를 쉼표로 구분합니다.
다음 코드에서는 앞 단원에서 정의한 함수를 사용하는 방법을 보여 줍니다.
// In this case, the type argument is inferred to be int.
function1 10 20
// In this case, the type argument is float.
function1 10.0 20.0
// Type arguments can be specified, but should only be specified
// if the type parameters are declared explicitly. If specified,
// they have an effect on type inference, so in this example,
// a and b are inferred to have type int.
let function3 a b =
// The compiler reports a warning:
function1<int> a b
// No warning.
function2<int> a b
참고
이름을 사용하여 제네릭 형식을 참조하는 데는 두 가지 방법이 있습니다. 예를 들어 int라는 단일 형식 인수를 갖는 제네릭 형식 list를 참조하는 데 list<int> 또는 int list라는 두 가지 방법을 사용할 수 있습니다. 둘째 형식은 일반적으로 list 및 option 같은 기본 제공 F# 형식에만 사용됩니다. 형식 인수가 여러 개인 경우 Dictionary<int, string> 구문을 사용하는 것이 일반적이지만 (int, string) Dictionary 구문을 사용할 수도 있습니다.
형식 인수로서의 와일드카드
컴파일러에서 형식 인수를 유추하도록 지정하려면 명명된 형식 인수 대신 와일드카드 기호인 밑줄(_)을 사용하면 됩니다. 이는 다음 코드에서 볼 수 있습니다.
let printSequence (sequence1: Collections.seq<_>) =
Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1
제네릭 형식 및 함수의 제약 조건
제네릭 형식이나 함수 정의에서는 제네릭 형식 매개 변수에 사용할 수 있는 것으로 알려진 구문만 사용할 수 있습니다. 이와 같이 알려진 구문만 사용해야 컴파일 타임에 함수 및 메서드 호출을 확인할 수 있기 때문입니다. 형식 매개 변수를 명시적으로 선언하는 경우 특정 메서드와 함수를 사용할 수 있다는 사실을 컴파일러에 알리기 위해 제네릭 형식 매개 변수에 명시적 제약 조건을 적용할 수 있습니다. 그러나 F# 컴파일러에서 제네릭 매개 변수 형식을 유추하도록 허용하면 적절한 제약 조건이 컴파일러를 통해 자동으로 결정됩니다. 자세한 내용은 제약 조건(F#)을 참조하십시오.
정적으로 확인된 형식 매개 변수
F# 프로그램에 사용할 수 있는 형식 매개 변수에는 두 가지 종류가 있습니다. 그중 하나는 앞 단원에서 설명한 종류의 제네릭 형식 매개 변수입니다. 이 유형의 형식 매개 변수는 Visual Basic 및 C# 같은 언어에 사용되는 제네릭 형식 매개 변수에 상응합니다. 다른 종류의 형식 매개 변수는 F#에만 사용되는 것으로서 정적으로 확인된 형식 매개 변수라고 합니다. 관련 구문에 대한 자세한 내용은 정적으로 확인된 형식 매개 변수(F#)를 참조하십시오.
예제
// A generic function.
// In this example, the generic type parameter 'a makes function3 generic.
let function3 (x : 'a) (y : 'a) =
printf "%A %A" x y
// A generic record, with the type parameter in angle brackets.
type GR<'a> =
{
Field1: 'a;
Field2: 'a;
}
// A generic class.
type C<'a>(a : 'a, b : 'a) =
let z = a
let y = b
member this.GenericMethod(x : 'a) =
printfn "%A %A %A" x y z
// A generic discriminated union.
type U<'a> =
| Choice1 of 'a
| Choice2 of 'a * 'a
type Test() =
// A generic member
member this.Function1<'a>(x, y) =
printfn "%A, %A" x, y
// A generic abstract method.
abstract abstractMethod<'a, 'b> : 'a * 'b -> unit
override this.abstractMethod<'a, 'b>(x:'a, y:'b) =
printfn "%A, %A" x y