Gibt es einen Anwendungsfall für den unteren Typ als Funktionsparametertyp?


12

Wenn eine Funktion den Rückgabetyp ⊥ ( unterer Typ ) hat, bedeutet dies, dass sie niemals zurückgibt. Es kann zum Beispiel aussteigen oder werfen, beides ganz gewöhnliche Situationen.

Vermutlich könnte eine Funktion mit einem Parameter vom Typ ⊥ niemals (sicher) aufgerufen werden. Gibt es jemals Gründe, eine solche Funktion zu definieren?

Antworten:


17

Eine der definierenden Eigenschaften des Typs oder leer ist, dass für jeden Typ A eine Funktion A existiert . In der Tat gibt es eine einzigartige solche Funktion. Es ist daher vernünftig, diese Funktion als Teil der Standardbibliothek bereitzustellen. Oft wird es so etwas genannt . (In Systemen mit Untertypisierung kann dies einfach dadurch gehandhabt werden, dass ein Untertyp jedes Typs ist. Dann lautet die implizite Konvertierung . Ein anderer verwandter Ansatz besteht darin , als α zu definieren . Α kann einfach zu jedem Typ instanziiert werden.)Aabsurdabsurd α.α

Sie möchten auf jeden Fall eine solche Funktion oder ein Äquivalent haben, weil Sie damit Funktionen nutzen können, die erzeugen . Zum Beispiel, sagen wir mal ich eine Summe Typ gegeben bin E+A . Ich mache eine Fallanalyse und im E Fall werde ich eine Ausnahme mit throw:E . Im A Fall würde ich verwenden f:AB . Insgesamt möchte ich einen Wert vom Typ B also muss ich etwas tun, um aus einem ein B . Das absurdwürde ich tun lassen.

Das heißt, es gibt nicht viele Gründe, Ihre eigenen Funktionen von A zu definieren . Per Definition wären sie notwendigerweise Instanzen von absurd. Sie können dies dennoch tun, wenn dies absurdnicht von der Standardbibliothek bereitgestellt wird, oder Sie möchten eine typspezialisierte Version zur Unterstützung der Typüberprüfung / Inferenz. Sie können jedoch leicht Funktionen erzeugen, die zu einem Typ wie A instanziiert werden .A

Auch wenn es kaum einen Grund gibt, eine solche Funktion zu schreiben, sollte sie im Allgemeinen immer noch erlaubt sein . Ein Grund ist, dass es Tools / Makros zur Codegenerierung vereinfacht.


Das heißt also, dass so etwas (x ? 3 : throw new Exception())für Analysezwecke durch so etwas wie ersetzt wird (x ? 3 : absurd(throw new Exception()))?
BDSL

Wenn Sie keinen hatten oder n't nicht als α definiert hatten . α , dann würde das erstere nicht checken und das letztere würde. Ja, mit Subtyping würde implizit so etwas eingefügt. Natürlich können Sie das Innere angeben, dessen Definition effektiv das ist, was als α definiert . α würde tun. α.αabsurdabsurdthrowα.α
Derek Elkins verließ SE

6

Um das, was über die Funktion gesagt wurde, zu ergänzen, habe absurd: ⊥ -> aich ein konkretes Beispiel, wo diese Funktion tatsächlich nützlich ist.

Betrachten Sie den Haskell-Datentyp, Free f ader eine allgemeine Baumstruktur mit fgeformten Knoten und Blättern darstellt, die as enthalten :

data Free f a = Op (f (Free f a)) | Var a

Diese Bäume können mit folgender Funktion gefaltet werden:

fold :: Functor f => (a -> b) -> (f b -> b) -> Free f a -> b
fold gen alg (Var x) = gen x
fold gen alg (Op x) = alg (fmap (fold gen alg) x)

Kurz gesagt, diese Operation findet algan den Knoten und genan den Blättern statt.

Nun zum Punkt: Alle rekursiven Datenstrukturen können mit einem Festkomma-Datentyp dargestellt werden. In Haskell ist Fix fund kann dies definiert werden als type Fix f = Free f ⊥(dh Bäume mit f-förmigen Knoten und keinen Blättern außerhalb des Funktors f). Traditionell hat diese Struktur auch eine Falte, genannt cata:

cata :: Functor f => (f a -> a) -> Fix f -> a
cata alg x = fold absurd alg x

Das ist eine ziemlich gute Verwendung des Absurden: Da der Baum keine Blätter haben kann (da ⊥ keine anderen Einwohner hat als undefined), ist es niemals möglich, die genfür diese Falte zu verwenden und das zu absurdveranschaulichen!


2

Der unterste Typ ist ein Untertyp jedes anderen Typs, was in der Praxis äußerst nützlich sein kann. Beispielsweise muss der Typ von NULLin einer theoretischen typsicheren Version von C ein Subtyp jedes anderen Zeigertyps sein, andernfalls können Sie z. B. nicht zurückgeben, NULLwo ein char*erwartet wurde. Ebenso muss der Typ von undefinedin der Theorie typsicherem JavaScript ein Subtyp jedes anderen Typs in der Sprache sein.

exit()throw()IntIntexit()

TST


3
NULLIst das nicht ein Einheitentyp, der sich von ⊥ unterscheidet? Welcher Typ ist leer?
BDSL

Ich bin mir nicht ganz sicher, was ≺ in der Typentheorie bedeutet.
BDSL

1
@bdsl Der gekrümmte Operator ist hier "ist ein Untertyp von"; Ich bin nicht sicher, ob es Standard ist, es ist genau das, was mein Professor verwendet hat.
Draconis

1
@ gnasher729 Stimmt, aber C ist auch nicht besonders typsicher. Ich sage, wenn Sie nicht einfach eine Ganzzahl in void*umwandeln könnten , würden Sie einen bestimmten Typ dafür benötigen, der für jeden Zeigertyp verwendet werden könnte.
Draconis


2

Ich kann mir eine Verwendung vorstellen, die als Verbesserung der Swift-Programmiersprache angesehen wurde.

Swift hat eine maybeMonade, Dinkel Optional<T>oder T?. Es gibt viele Möglichkeiten, damit zu interagieren.

  • Sie können das bedingte Auspacken wie verwenden

    if let nonOptional = someOptional {
        print(nonOptional)
    }
    else {
        print("someOptional was nil")
    }
    
  • Sie können verwendet werden map, um flatMapdie Werte zu transformieren

  • Der Operator "Force Unwrap" ( !vom Typ " Force Unwrap" (T?) -> T), um das gewaltsame Auspacken des Inhalts zu erzwingen, andernfalls wird ein Absturz ausgelöst
  • Der Null-Koaleszenz-Operator ( ??vom Typ (T?, T) -> T), um seinen Wert zu übernehmen oder auf andere Weise einen Standardwert zu verwenden:

    let someI = Optional(100)
    print(someI ?? 123) => 100 // "left operand is non-nil, unwrap it.
    
    let noneI: Int? = nil
    print(noneI ?? 123) // => 123 // left operand is nil, take right operand, acts like a "default" value
    

Leider gab es keine prägnante Möglichkeit zu sagen: "Fehler auspacken oder auslösen" oder "Mit einer benutzerdefinierten Fehlermeldung auspacken oder abstürzen". Etwas wie

let someI: Int? = Optional(123)
let nonOptionalI: Int = someI ?? fatalError("Expected a non-nil value")

Kompiliert nicht, weil fatalErrores den Typ hat () -> Never( ()ist Voidder Einheitentyp von NeverSwift, der unterste Typ von Swift). Der Aufruf erzeugt Never, was nicht mit dem Terwarteten richtigen Operanden von kompatibel ist ??.

Um dem abzuhelfen, wurde der Vorschlag von Swift Evolution SE-0217- The Unwrap or Die veröffentlicht . Es wurde letztendlich abgelehnt , aber es weckte das Interesse, Nevereinen Untertyp aller Art zu machen.

Wenn Neverals Subtyp aller Typen festgelegt wurde, kann das vorherige Beispiel kompiliert werden:

let someI: Int? = Optional(123)
let nonOptionalI: Int = someI ?? fatalError("Expected a non-nil value")

weil die aufrufstelle von ??typ hat (T?, Never) -> T, der mit der (T?, T) -> Tsignatur von kompatibel wäre ??.


0

Swift hat einen Typ "Nie", der dem untersten Typ ziemlich ähnlich zu sein scheint: Eine Funktion, die als "Zurückgeben" deklariert ist, kann nie zurückgegeben werden, eine Funktion mit einem Parameter vom Typ "Nie" kann nie aufgerufen werden.

Dies ist nützlich in Verbindung mit Protokollen, bei denen es aufgrund des Typsystems der Sprache zu einer Einschränkung kommen kann, dass eine Klasse eine bestimmte Funktion haben muss, ohne dass diese Funktion jemals aufgerufen werden muss und ohne dass die Argumenttypen angegeben werden müssen wäre.

Für Details sollten Sie einen Blick auf die neueren Beiträge in der Mailingliste von swift-evolution werfen.


7
"Neuere Posts auf der Swift-Evolution-Mailingliste" ist keine sehr klare oder stabile Referenz. Gibt es kein Webarchiv der Mailingliste?
Derek Elkins verließ SE
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.