Suche…


Einführung in Typen

Typen können verschiedene Arten von Dingen darstellen. Es können einzelne Daten, ein Datensatz oder eine Funktion sein.

In F # können wir die Typen in zwei Kategorien einteilen:

  • F # Typen:

     // 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-Typen

    • Eingebauter Typ (int, bool, string, ...)
    • Klassen, Strukturen und Schnittstellen
    • Delegierte
    • Arrays

Typ Abkürzungen

Mit Typabkürzungen können Sie Aliase für vorhandene Typen erstellen, um ihnen einen sinnvollen Sinn zu geben.

// Name is an alias for a string
type Name = string

// PhoneNumber is an alias for a string
type PhoneNumber = string

Dann können Sie den Alias ​​wie jeden anderen Typ verwenden:

// 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";}

Seien Sie vorsichtig, Aliase prüfen nicht auf Typkonsistenz. Das bedeutet, dass zwei Aliase, die auf denselben Typ abzielen, einander zugewiesen werden können:

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";}

Typen werden in F # mit dem Schlüsselwort type erstellt

F# verwendet das Schlüsselwort type , um verschiedene Arten von Typen zu erstellen.

  1. Geben Sie Aliase ein
  2. Diskriminierte Gewerkschaftstypen
  3. Aufnahmetypen
  4. Schnittstellentypen
  5. Klassenarten
  6. Strukturtypen

Beispiele mit gleichwertigem C# -Code, soweit möglich:

// 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

Typ Inferenz

Wissen

Dieses Beispiel wurde aus diesem Artikel zur Typeninferenz angepasst

Was ist Typ Inferenz?

Type Inference ist der Mechanismus, mit dem der Compiler ableiten kann, welche Typen wo verwendet werden. Dieser Mechanismus basiert auf einem Algorithmus, der häufig als "Hindley-Milner" oder "HM" bezeichnet wird. Nachfolgend einige Regeln zum Ermitteln der Typen von einfachen Werten und Funktionswerten:

  • Schau dir die Literale an
  • Schauen Sie sich die Funktionen und andere Werte an, mit denen etwas interagiert
  • Sehen Sie sich explizite Typeinschränkungen an
  • Wenn es nirgends irgendwelche Einschränkungen gibt, generalisieren Sie automatisch auf generische Typen

Schau dir die Literale an

Der Compiler kann Typen anhand der Literale ableiten. Wenn das Literal ein int ist und Sie "x" hinzufügen, muss "x" ebenfalls ein int sein. Wenn das Literal jedoch ein Float ist und Sie "x" hinzufügen, muss "x" ebenfalls ein Float sein.

Hier sind einige Beispiele:

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"

Schauen Sie sich die Funktionen und andere Werte an, mit denen sie interagieren

Wenn nirgendwo Literale vorhanden sind, versucht der Compiler, die Typen zu ermitteln, indem er die Funktionen und andere Werte analysiert, mit denen sie interagieren.

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

Sehen Sie sich explizite Typeinschränkungen oder Anmerkungen an

Wenn explizite Typeinschränkungen oder Anmerkungen angegeben wurden, verwendet der Compiler diese.

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

Automatische Generalisierung

Wenn nach all dem keine Einschränkungen gefunden werden, macht der Compiler die Typen nur zu generisch.

let inferGeneric x = x 
let inferIndirectGeneric x = inferGeneric x 
let inferIndirectGenericAgain x = (inferIndirectGeneric x).ToString() 

Dinge, die durch Typeninferenz schief gehen können

Der Typ Inferenz ist leider nicht perfekt. Manchmal hat der Compiler keine Ahnung, was er tun soll. Wenn Sie wissen, was passiert, können Sie wirklich ruhig bleiben, anstatt den Compiler töten zu wollen. Hier sind einige der Hauptgründe für Typfehler:

  • Erklärungen außer Betrieb
  • Nicht genug Information
  • Überlastete Methoden

Erklärungen außer Betrieb

Eine grundlegende Regel ist, dass Sie Funktionen deklarieren müssen, bevor sie verwendet werden.

Dieser Code schlägt fehl:

let square2 x = square x   // fails: square not defined
let square x = x * x

Das ist aber ok:

let square x = x * x       
let square2 x = square x   // square already defined earlier

Rekursive oder gleichzeitige Deklarationen

Eine Variante des Problems "Außer Betrieb" tritt bei rekursiven Funktionen oder Definitionen auf, die sich aufeinander beziehen müssen. In diesem Fall hilft keine Neuordnung. Wir müssen zusätzliche Schlüsselwörter verwenden, um den Compiler zu unterstützen.

Wenn eine Funktion kompiliert wird, ist der Funktionsbezeichner für den Körper nicht verfügbar. Wenn Sie also eine einfache rekursive Funktion definieren, wird ein Compiler-Fehler angezeigt. Das Update besteht darin, das Schlüsselwort "rec" als Teil der Funktionsdefinition hinzuzufügen. Zum Beispiel:

// 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

Hier ist die feste Version mit "rec fib" hinzugefügt, um anzuzeigen, dass sie rekursiv ist:

let rec fib n =              // LET REC rather than LET
   if n <= 2 then 1
   else fib (n - 1) + fib (n - 2)

Nicht genug Information

Manchmal hat der Compiler nicht genügend Informationen, um einen Typ zu bestimmen. Im folgenden Beispiel weiß der Compiler nicht, mit welchem ​​Typ die Length-Methode arbeiten soll. Aber es kann es auch nicht generisch machen, klagt es.

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 ...

Diese Fehler können durch explizite Anmerkungen behoben werden.

let stringLength (s:string) = s.Length

Überlastete Methoden

Wenn Sie eine externe Klasse oder Methode in .NET aufrufen, erhalten Sie häufig Fehler aufgrund von Überladung.

In vielen Fällen, z. B. im folgenden concat-Beispiel, müssen Sie die Parameter der externen Funktion explizit kommentieren, damit der Compiler weiß, welche überladene Methode aufgerufen wird.

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

Manchmal haben die überladenen Methoden andere Argumentnamen. In diesem Fall können Sie dem Compiler auch einen Hinweis geben, indem Sie die Argumente benennen. Hier ist ein Beispiel für den StreamReader-Konstruktor.

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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow