다음을 통해 공유


자동 일반화(F#)

F#에서 함수와 식의 형식을 확인하는 데는 형식 유추 방식이 사용됩니다. 이 항목에서는 함수가 여러 가지 형식으로 작동할 수 있는 경우 이를 구현하기 위해 F#에서 함수의 인수와 형식을 자동으로 일반화하는 방식에 대해 설명합니다.

자동 일반화

F# 컴파일러는 함수의 형식을 유추할 때 해당 매개 변수가 제네릭일 수 있는지 여부를 확인합니다. 컴파일러는 각 매개 변수를 조사하여 함수에 사용되는 해당 매개 변수가 특정 형식이어야 하는지 여부를 확인합니다. 함수에 특정 형식이 필요하지 않으면 형식이 제네릭인 것으로 유추됩니다.

다음 코드 예제에서는 컴파일러를 통해 제네릭인 것으로 유추되는 함수를 보여 줍니다.

let max a b = if a > b then a else b

형식은 'a -> 'a -> 'a로 유추됩니다.

이는 알려져 있지 않은 동일한 형식의 인수 두 개가 함수에 사용되고 함수를 통해 동일한 형식의 값이 반환됨을 의미하는 형식입니다. 위 함수가 제네릭일 수 있는 이유 중 하나는 보다 큼 연산자(>) 자체가 제네릭이라는 데 있습니다. 보다 큼 연산자에는 'a -> 'a -> bool 시그니처가 있습니다. 모든 연산자가 제네릭인 것은 아닙니다. 함수 코드에서 제네릭이 아닌 함수 또는 연산자와 함께 매개 변수 형식을 사용하는 경우 해당 매개 변수 형식은 일반화할 수 없습니다.

max는 제네릭이므로 다음 예제에서와 같이 int, float 등의 형식과 함께 사용할 수 있습니다.

let biggestFloat = max 2.0 3.0
let biggestInt = max 2 3

그러나 두 인수는 형식이 같아야 합니다. 시그니처는 'a -> 'b -> 'a가 아니라 'a -> 'a -> 'a입니다. 따라서 다음 코드에서는 형식이 일치하지 않으므로 오류가 발생합니다.

// Error: type mismatch.
let biggestIntFloat = max 2.0 3

max 함수는 보다 큼 연산자를 지원하는 모든 형식에 사용할 수 있습니다. 따라서 다음 코드에서와 같이 문자열에 대해서도 이 함수를 사용할 수 있습니다.

let testString = max "cab" "cat"

값 제한

컴파일러는 명시적 인수가 있는 완전한 함수 정의와 변경 불가능한 단순한 값에 대해서만 자동 일반화를 수행합니다.

즉, 특정 형식으로 충분히 제한되지도 않았고 일반화할 수도 없는 코드를 컴파일하려고 하면 컴파일러에서 오류가 발생합니다. 이 문제와 관련된 오류 메시지에서는 값에 대한 자동 일반화의 이와 같은 제한 사항을 가리키는 데 값 제한이라는 표현을 사용합니다.

일반적으로 값 제한 오류는 구문을 제네릭으로 설정하려고 하지만 정보가 충분하지 않아 컴파일러에서 해당 구문을 일반화할 수 없는 경우나 제네릭이 아닌 구문에서 실수로 충분한 형식 정보를 제공하지 않은 경우에 발생합니다. 값 제한 오류를 해결하려면 명시적 정보를 추가로 제공하여 형식 유추 문제를 더 완벽하게 제한해야 합니다. 이때 다음 방법 중 하나를 사용할 수 있습니다.

  • 값이나 매개 변수에 명시적 형식 주석을 추가하여 형식을 제네릭이 아닌 것으로 제한합니다.

  • 함수 합성 또는 불완전하게 적용된 변환 함수 인수 등과 같이 일반화할 수 없는 구문을 사용하여 제네릭 함수를 정의하려고 시도하여 문제가 발생한 경우에는 일반적인 함수 정의를 사용하여 함수를 다시 작성합니다.

  • 식이 너무 복잡하여 일반화할 수 없는 데 따른 문제이면 사용되지 않는 매개 변수를 추가하여 식을 함수로 만듭니다.

  • 명시적 제네릭 형식 매개 변수를 추가합니다. 이 옵션은 거의 사용되지 않습니다.

  • 다음 코드 예제에서는 이러한 각 시나리오를 보여 줍니다.

사례 1: 너무 복잡한 식. 이 예제에서 counter 목록은 int option ref로 사용하기 위한 것이지만, 해당 목록이 변경 불가능한 단순한 값으로 정의되어 있지 않습니다.

let counter = ref None
// Adding a type annotation fixes the problem:
let counter : int option ref = ref None

사례 2: 일반화할 수 없는 구문을 사용하여 제네릭 함수 정의. 이 예제에 나오는 구문은 함수 인수가 부분 적용되고 있으므로 일반화할 수 없습니다.

let maxhash = max << hash
// The following is acceptable because the argument for maxhash is explicit:
let maxhash obj = (max << hash) obj

사례 3: 사용되지 않는 불필요한 매개 변수 추가. 이 식은 일반화할 수 있을 만큼 간단하지 않으므로 컴파일러에서 값 제한 오류가 발생합니다.

let emptyList10 = Array.create 10 []
// Adding an extra (unused) parameter makes it a function, which is generalizable.
let emptyList10 () = Array.create 10 []

사례 4: 형식 매개 변수 추가.

let arrayOf10Lists = Array.create 10 []
// Adding a type parameter and type annotation lets you write a generic value.
let arrayOf10Lists<'T> = Array.create 10 ([]:'T list)

마지막 경우에 많은 다른 종류의 값을 예를 들어 다음과 같은 만드는 데 사용할 수 있는 형식 함수 값이 됩니다.

let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>

참고 항목

참조

형식 유추(F#)

제네릭(F#)

정적으로 확인된 형식 매개 변수(F#)

제약 조건(F#)