수색…


유형 소개

유형은 다양한 종류의 것을 나타낼 수 있습니다. 단일 데이터, 데이터 세트 또는 기능 일 수 있습니다.

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 키워드를 사용하여 다른 종류의 유형을 만듭니다.

  1. 별칭 유형
  2. 차별화 된 공용체 유형
  3. 레코드 유형
  4. 인터페이스 유형
  5. 클래스 유형
  6. 구조체 유형

가능한 경우 동일한 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


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow