정적으로 확인된 형식 매개 변수

정적으로 확인된 형식 매개 변수는 런타임이 아닌 컴파일 시간에 실제 형식으로 대체되는 형식 매개 변수입니다.

구문

'type-parameter

F#의 버전 7.0까지 다음 구문을 사용해야 했습니다.

^type-parameter

설명

F#에는 두 가지 종류의 형식 매개 변수가 있습니다. 첫 번째 종류는 표준 제네릭 형식 매개 변수입니다. 다른 .NET 언어의 제네릭 형식 매개 변수와 동일합니다. 다른 종류는 정적으로 확인되며 인라인 함수에서만 사용할 수 있습니다.

정적으로 확인된 형식 매개 변수는 주로 멤버 제약 조건과 함께 유용합니다. 이 제약 조건은 형식 인수를 사용하려면 특정 멤버 또는 멤버를 포함하도록 지정할 수 있는 제약 조건입니다. 일반 제네릭 형식 매개 변수를 사용하여 이러한 종류의 제약 조건을 만들 수 있는 방법은 없습니다.

다음 표에는 두 종류의 형식 매개 변수 간의 유사점과 차이점이 요약되어 있습니다.

기능 일반 정적으로 해결됨
해결 시간 실행 시간 컴파일 시간
멤버 제약 조건 멤버 제약 조건과 함께 사용할 수 없습니다. 멤버 제약 조건과 함께 사용할 수 있습니다.
코드 생성 표준 제네릭 형식 매개 변수가 있는 형식(또는 메서드)은 단일 제네릭 형식 또는 메서드를 생성합니다. 필요한 각 형식에 대해 하나씩 여러 형식 및 메서드 인스턴스화가 생성됩니다.
형식과 함께 사용 형식에 사용할 수 있습니다. 형식에서는 사용할 수 없습니다.
인라인 함수와 함께 사용 인라인 함수는 표준 제네릭 형식 매개 변수를 사용하여 매개 변수화할 수 없습니다. 입력이 완전히 제네릭이 아닌 경우 F# 컴파일러는 입력을 특수화하거나 특수화할 옵션이 없는 경우 오류를 발생합니다. 정적으로 확인된 형식 매개 변수는 인라인이 아닌 함수 또는 메서드에서 사용할 수 없습니다.

많은 F# 코어 라이브러리 함수, 특히 연산자는 정적으로 확인된 형식 매개 변수를 가지고 있습니다. 이러한 함수와 연산자는 인라인이며 숫자 계산을 위한 효율적인 코드 생성을 초래합니다.

연산자를 사용하거나 정적으로 확인된 형식 매개 변수가 있는 다른 함수를 사용하는 인라인 메서드 및 함수는 정적으로 확인된 형식 매개 변수 자체를 사용할 수도 있습니다. 종종 형식 유추는 이러한 인라인 함수를 유추하여 정적으로 확인된 형식 매개 변수를 갖습니다. 다음 예제에서는 정적으로 확인된 형식 매개 변수를 갖도록 유추되는 연산자 정의를 보여 줍니다.

let inline (+@) x y = x + x * y
// Call that uses int.
printfn "%d" (1 +@ 1)
// Call that uses float.
printfn "%f" (1.0 +@ 0.5)

확인된 형식 (+@) 은 둘 다 (+) 의 사용을 기반으로 하며 (*), 둘 다 형식 유추를 통해 정적으로 확인된 형식 매개 변수에 대한 멤버 제약 조건을 유추합니다. F# 인터프리터에 표시된 것처럼 확인된 형식은 다음과 같습니다.

'a -> 'c -> 'd
when ('a or 'b) : (static member ( + ) : 'a * 'b -> 'd) and
('a or 'c) : (static member ( * ) : 'a * 'c -> 'b)

출력은 다음과 같습니다.

2
1.500000

다음 예제에서는 메서드 및 정적 메서드를 사용하여 SRTP를 사용하는 방법을 보여 줍니다.

type Record =
    { Number: int }
    member this.Double() = { Number = this.Number * 2 }
    static member Zero() = { Number = 0 }
    
let inline double<'a when 'a:(member Double: unit -> 'a)> (x: 'a) = x.Double()    
let inline zero<'a when 'a:(static member Zero: unit -> 'a)> () = 'a.Zero()

let r: Record = zero ()
let doubleR = double r

F# 7.0부터는 아래 예제와 같이 제약 조건을 반복하는 대신 사용할 'a.Zero() 수 있습니다.

F# 4.1부터 정적으로 확인된 형식 매개 변수 서명에서 구체적인 형식 이름을 지정할 수도 있습니다. 이전 버전의 언어에서는 형식 이름이 컴파일러에서 유추되었지만 서명에 지정할 수 없습니다. F# 4.1을 기준으로 정적으로 확인된 형식 매개 변수 서명에 구체적인 형식 이름을 지정할 수도 있습니다. 다음은 예제입니다(이 예제 ^ 에서는 사용 간소화가 지원되지 않으므로 계속 사용해야 ' 함).

let inline konst x _ = x

type CFunctor() =
    static member inline fmap (f: ^a -> ^b, a: ^a list) = List.map f a
    static member inline fmap (f: ^a -> ^b, a: ^a option) =
        match a with
        | None -> None
        | Some x -> Some (f x)

    // default implementation of replace
    static member inline replace< ^a, ^b, ^c, ^d, ^e when ^a :> CFunctor and (^a or ^d): (static member fmap: (^b -> ^c) * ^d -> ^e) > (a, f) =
        ((^a or ^d) : (static member fmap : (^b -> ^c) * ^d -> ^e) (konst a, f))

    // call overridden replace if present
    static member inline replace< ^a, ^b, ^c when ^b: (static member replace: ^a * ^b -> ^c)>(a: ^a, f: ^b) =
        (^b : (static member replace: ^a * ^b -> ^c) (a, f))

let inline replace_instance< ^a, ^b, ^c, ^d when (^a or ^c): (static member replace: ^b * ^c -> ^d)> (a: ^b, f: ^c) =
        ((^a or ^c): (static member replace: ^b * ^c -> ^d) (a, f))

// Note the concrete type 'CFunctor' specified in the signature
let inline replace (a: ^a) (f: ^b): ^a0 when (CFunctor or  ^b): (static member replace: ^a *  ^b ->  ^a0) =
    replace_instance<CFunctor, _, _, _> (a, f)

참고 항목