Kann mir jemand sagen, warum das Haskell-Präludium zwei separate Funktionen für die Potenzierung definiert (dh ^und **)? Ich dachte, das Typensystem sollte diese Art der Vervielfältigung beseitigen.
Prelude> 2^2
4
Prelude> 4**0.5
2.0
Kann mir jemand sagen, warum das Haskell-Präludium zwei separate Funktionen für die Potenzierung definiert (dh ^und **)? Ich dachte, das Typensystem sollte diese Art der Vervielfältigung beseitigen.
Prelude> 2^2
4
Prelude> 4**0.5
2.0
Antworten:
Es gibt eigentlich drei Potenzierung Betreiber: (^), (^^)und (**). ^ist eine nicht negative integrale Exponentiation, ^^eine ganzzahlige Exponentiation und **eine Gleitkomma-Exponentiation:
(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a
Der Grund ist die Typensicherheit: Ergebnisse numerischer Operationen haben im Allgemeinen den gleichen Typ wie die Eingabeargumente. Sie können jedoch keine Intauf eine Gleitkomma-Potenz erhöhen und ein typisches Ergebnis erhalten Int. Das Typsystem verhindert dies und verhindert (1::Int) ** 0.5einen Typfehler. Das gilt auch für (1::Int) ^^ (-1).
Eine andere Möglichkeit, dies auszudrücken: NumTypen werden unter geschlossen ^(sie müssen keine multiplikative Inverse haben), FractionalTypen werden unter geschlossen ^^, FloatingTypen werden unter geschlossen **. Da es keine FractionalInstanz für gibt Int, können Sie sie nicht auf eine negative Potenz erhöhen.
Idealerweise wäre das zweite Argument von ^statisch darauf beschränkt, nicht negativ zu sein ( 1 ^ (-2)löst derzeit eine Laufzeitausnahme aus). Aber es gibt keinen Typ für natürliche Zahlen in der Prelude.
Das Typsystem von Haskell ist nicht leistungsfähig genug, um die drei Potenzierungsoperatoren als einen auszudrücken. Was Sie wirklich wollen, ist ungefähr so:
class Exp a b where (^) :: a -> b -> a
instance (Num a, Integral b) => Exp a b where ... -- current ^
instance (Fractional a, Integral b) => Exp a b where ... -- current ^^
instance (Floating a, Floating b) => Exp a b where ... -- current **
Dies funktioniert nicht wirklich, selbst wenn Sie die Klassenerweiterung mit mehreren Parametern aktivieren, da die Instanzauswahl klüger sein muss, als Haskell derzeit zulässt.
Intals als auch ist Integer. Um diese drei Instanzdeklarationen zu erhalten, muss die Instanzauflösung Backtracking verwenden, und kein Haskell-Compiler implementiert dies.
Es definiert nicht zwei Operatoren - es definiert drei! Aus dem Bericht:
Es gibt drei Exponentiationsoperationen mit zwei Argumenten: (
^) erhöht eine beliebige Zahl auf eine nichtnegative ganzzahlige Potenz, (^^) erhöht eine gebrochene Zahl auf eine ganzzahlige Potenz und (**) verwendet zwei Gleitkommaargumente. Der Wert vonx^0oderx^^0ist 1 für allex, einschließlich Null;0**yist nicht definiert.
Dies bedeutet, dass es drei verschiedene Algorithmen gibt, von denen zwei genaue Ergebnisse ( ^und ^^) liefern , während sie **ungefähre Ergebnisse liefern. Durch Auswahl des zu verwendenden Operators wählen Sie den aufzurufenden Algorithmus aus.
^erfordert, dass sein zweites Argument ein ist Integral. Wenn ich mich nicht irre, kann die Implementierung effizienter sein, wenn Sie wissen, dass Sie mit einem integralen Exponenten arbeiten. Wenn Sie so etwas wie möchten 2 ^ (1.234), obwohl Ihre Basis ein Integral 2 ist, ist Ihr Ergebnis offensichtlich ein Bruchteil. Sie haben mehr Optionen, damit Sie genauer steuern können, welche Typen in Ihre Exponentiationsfunktion ein- und ausgehen.
Das Typsystem von Haskell hat nicht das gleiche Ziel wie andere Typsysteme wie C, Python oder Lisp. Das Tippen von Enten ist (fast) das Gegenteil der Haskell-Denkweise.
class Duck a where quack :: a -> Quackdefiniert, was wir von einer Ente erwarten, und dann gibt jede Instanz etwas an, das sich wie eine Ente verhalten kann.
Duck.