수색…
유형 소개
유형은 다양한 종류의 것을 나타낼 수 있습니다. 단일 데이터, 데이터 세트 또는 기능 일 수 있습니다.
F #에서는 두 유형으로 유형을 그룹화 할 수 있습니다.
F # 유형 :
// Functions let a = fun c -> c // Tuples let b = (0, "Foo") // Unit type let c = ignore // Records type r = { Name : string; Age : int } let d = { Name = "Foo"; Age = 10 } // Discriminated Unions type du = | Foo | Bar let e = Bar // List and seq let f = [ 0..10 ] let g = seq { 0..10 } // Aliases type MyAlias = string
.NET 유형
- 내장 유형 (int, bool, string, ...)
- 클래스, 구조체 및 인터페이스
- 대표자들
- 배열
유형 약어
유형 약어를 사용하면 기존 유형에 별명을 작성하여 의미있는 감각을 부여 할 수 있습니다.
// Name is an alias for a string
type Name = string
// PhoneNumber is an alias for a string
type PhoneNumber = string
그런 다음 다른 유형과 마찬가지로 별칭을 사용할 수 있습니다.
// Create a record type with the alias
type Contact = {
Name : Name
Phone : PhoneNumber }
// Create a record instance
// We can assign a string since Name and PhoneNumber are just aliases on string type
let c = {
Name = "Foo"
Phone = "00 000 000" }
printfn "%A" c
// Output
// {Name = "Foo";
// Phone = "00 000 000";}
별칭은 형식 일관성을 검사하지 않으므로주의하십시오. 즉, 동일한 유형을 목표로하는 두 개의 별칭을 서로 할당 할 수 있습니다.
let c = {
Name = "Foo"
Phone = "00 000 000" }
let d = {
Name = c.Phone
Phone = c.Name }
printfn "%A" d
// Output
// {Name = "00 000 000";
// Phone = "Foo";}
유형은 F #에서 유형 키워드를 사용하여 작성됩니다.
F#
은 type
키워드를 사용하여 다른 종류의 유형을 만듭니다.
- 별칭 유형
- 차별화 된 공용체 유형
- 레코드 유형
- 인터페이스 유형
- 클래스 유형
- 구조체 유형
가능한 경우 동일한 C#
코드의 예제 :
// Equivalent C#:
// using IntAliasType = System.Int32;
type IntAliasType = int // As in C# this doesn't create a new type, merely an alias
type DiscriminatedUnionType =
| FirstCase
| SecondCase of int*string
member x.SomeProperty = // We can add members to DU:s
match x with
| FirstCase -> 0
| SecondCase (i, _) -> i
type RecordType =
{
Id : int
Name : string
}
static member New id name : RecordType = // We can add members to records
{ Id = id; Name = name } // { ... } syntax used to create records
// Equivalent C#:
// interface InterfaceType
// {
// int Id { get; }
// string Name { get; }
// int Increment (int i);
// }
type InterfaceType =
interface // In order to create an interface type, can also use [<Interface>] attribute
abstract member Id : int
abstract member Name : string
abstract member Increment : int -> int
end
// Equivalent C#:
// class ClassType : InterfaceType
// {
// static int increment (int i)
// {
// return i + 1;
// }
//
// public ClassType (int id, string name)
// {
// Id = id ;
// Name = name ;
// }
//
// public int Id { get; private set; }
// public string Name { get; private set; }
// public int Increment (int i)
// {
// return increment (i);
// }
// }
type ClassType (id : int, name : string) = // a class type requires a primary constructor
let increment i = i + 1 // Private helper functions
interface InterfaceType with // Implements InterfaceType
member x.Id = id
member x.Name = name
member x.Increment i = increment i
// Equivalent C#:
// class SubClassType : ClassType
// {
// public SubClassType (int id, string name) : base(id, name)
// {
// }
// }
type SubClassType (id : int, name : string) =
inherit ClassType (id, name) // Inherits ClassType
// Equivalent C#:
// struct StructType
// {
// public StructType (int id)
// {
// Id = id;
// }
//
// public int Id { get; private set; }
// }
type StructType (id : int) =
struct // In order create a struct type, can also use [<Struct>] attribute
member x.Id = id
end
유형 추론
승인
이 예제는이 기사의 형식 유추 에 적용됩니다.
타입 추론이란 무엇입니까?
타입 추론 (Type Inference)은 컴파일러가 어떤 타입이 사용되고 어디에 추론 할 수있게 해주는 메커니즘입니다. 이 메커니즘은 종종 "Hindley-Milner"또는 "HM"이라고하는 알고리즘을 기반으로합니다. 단순 및 함수 값의 유형을 결정하기위한 규칙 중 일부를 아래에서보십시오.
- 리터럴을보세요.
- 상호 작용하는 기능 및 기타 가치를 살펴보십시오.
- 모든 명시 적 유형 제약 조건을 살펴보십시오.
- 제약 조건이 없으면 자동으로 제네릭 형식으로 일반화합니다.
리터럴을보세요.
컴파일러는 리터럴을보고 형식을 추론 할 수 있습니다. 리터럴이 int이고 "x"를 추가하는 경우 "x"도 int 여야합니다. 그러나 리터럴이 float이고 "x"를 추가하는 경우 "x"는 float이어야합니다.
여기 예시들이 있습니다 :
let inferInt x = x + 1
let inferFloat x = x + 1.0
let inferDecimal x = x + 1m // m suffix means decimal
let inferSByte x = x + 1y // y suffix means signed byte
let inferChar x = x + 'a' // a char
let inferString x = x + "my string"
함수 및 함수와 상호 작용하는 다른 값을 살펴보십시오.
어디에서나 리터럴이 없으면 컴파일러는 상호 작용하는 함수 및 다른 값을 분석하여 유형을 산출하려고합니다.
let inferInt x = x + 1
let inferIndirectInt x = inferInt x //deduce that x is an int
let inferFloat x = x + 1.0
let inferIndirectFloat x = inferFloat x //deduce that x is a float
let x = 1
let y = x //deduce that y is also an int
명시 적 유형 제약 또는 주석을 살펴보십시오.
명시 적 타입 제약이나 주석이 있다면, 컴파일러는 그것을 사용할 것입니다.
let inferInt2 (x:int) = x // Take int as parameter
let inferIndirectInt2 x = inferInt2 x // Deduce from previous that x is int
let inferFloat2 (x:float) = x // Take float as parameter
let inferIndirectFloat2 x = inferFloat2 x // Deduce from previous that x is float
자동 일반화
결국이 제약 조건이 발견되지 않으면 컴파일러는 유형을 generic으로 만듭니다.
let inferGeneric x = x
let inferIndirectGeneric x = inferGeneric x
let inferIndirectGenericAgain x = (inferIndirectGeneric x).ToString()
형식 유추로 잘못 될 수있는 것들
타입 추론은 완벽하지 않습니다. 아아. 가끔은 컴파일러가 어떻게 해야할지 모른다. 다시 말하지만, 실제로 무슨 일이 일어나고 있는지 이해하면 컴파일러를 죽이기보다 침착하게 도움이 될 것입니다. 다음은 유형 오류의 주요 원인 중 일부입니다.
- 부적절한 선언
- 정보가 충분하지 않습니다.
- 오버로드 된 메서드
부적절한 선언
함수를 사용하기 전에 함수를 선언해야한다는 기본 규칙이 있습니다.
이 코드는 실패합니다.
let square2 x = square x // fails: square not defined
let square x = x * x
그러나 이것은 괜찮습니다.
let square x = x * x
let square2 x = square x // square already defined earlier
재귀 또는 동시 선언
"순서가 잘못된"문제의 변형은 서로를 참조해야하는 재귀 함수 또는 정의에서 발생합니다. 이 경우에는 순서 재 지정이 도움이되지 않습니다. 컴파일러를 돕기 위해 추가 키워드를 사용해야합니다.
함수가 컴파일 될 때 함수 식별자는 본문에서 사용할 수 없습니다. 따라서 간단한 재귀 함수를 정의하면 컴파일러 오류가 발생합니다. 수정 사항은 함수 정의의 일부로 "rec"키워드를 추가하는 것입니다. 예 :
// the compiler does not know what "fib" means
let fib n =
if n <= 2 then 1
else fib (n - 1) + fib (n - 2)
// error FS0039: The value or constructor 'fib' is not defined
다음은 재귀 적임을 나타 내기 위해 "rec fib"가 추가 된 고정 버전입니다.
let rec fib n = // LET REC rather than LET
if n <= 2 then 1
else fib (n - 1) + fib (n - 2)
정보가 충분하지 않습니다.
경우에 따라 컴파일러에서 유형을 결정하는 데 필요한 정보가 충분하지 않은 경우도 있습니다. 다음 예제에서 컴파일러는 Length 메서드가 작동해야하는 형식을 모릅니다. 그러나 일반화 할 수는 없으므로 불평합니다.
let stringLength s = s.Length
// error FS0072: Lookup on object of indeterminate type
// based on information prior to this program point.
// A type annotation may be needed ...
이러한 종류의 오류는 명시 적 주석으로 해결할 수 있습니다.
let stringLength (s:string) = s.Length
오버로드 된 메서드
.NET에서 외부 클래스 또는 메서드를 호출 할 때 오버로드로 인해 종종 오류가 발생합니다.
아래의 concat 예제와 같이 대부분의 경우 컴파일러에서 호출 할 오버로드 된 메서드를 알 수 있도록 외부 함수의 매개 변수에 명시 적으로 주석을 추가해야합니다.
let concat x = System.String.Concat(x) //fails
let concat (x:string) = System.String.Concat(x) //works
let concat x = System.String.Concat(x:string) //works
때로는 오버로드 된 메서드가 다른 인수 이름을 가지며,이 경우 컴파일러에게 인수의 이름을 지정하여 단서를 제공 할 수도 있습니다. 다음은 StreamReader 생성자의 예입니다.
let makeStreamReader x = new System.IO.StreamReader(x) //fails
let makeStreamReader x = new System.IO.StreamReader(path=x) //works