2012-05-15 19 views
20

Hilft es dem Compiler, zu optimieren, oder ist es nur überflüssige Arbeit, zusätzliche Typ-Signaturen hinzuzufügen? Zum Beispiel sieht man oft:Warum ist es so ungewöhnlich, in where-Klauseln Typ-Signaturen zu verwenden?

foo :: a -> b 
foo x = bar x 
     where bar x = undefined 

Statt:

foo :: a -> b 
foo x = bar x 
     where bar :: a -> b 
      bar x = undefined 

Wenn ich die Top-Level-Typ Unterschrift weglassen, GHC gibt mir eine Warnung, wenn ich also ich bin nicht ganz Warnungen erhalten zuversichtlich, dass mein Programm korrekt ist. Es werden jedoch keine Warnungen ausgegeben, wenn ich die Signatur in einer WHERE-Klausel weglasse.

+0

Ich kann die Frage nicht beantworten, ob es Kompilieren schneller macht oder nicht, aber es ist immer gute Praxis, Typ-Signaturen auszugeben, wenn es eine nicht-triviale Funktion ist. – Wes

+7

Außerdem müssen viele dieser lokalen Definitionen auf Typvariablen aus dem äußeren Bereich zugreifen, was dazu führt, dass der Code mit 'asTypeOf' und Freunden überfüllt wird, es sei denn, Sie verwenden' ScopedTypeVariables'. – Vitus

+1

Wenn Ihre Funktion wichtig genug ist, um eine Typdeklaration zu erhalten, warum sollten Sie sie nicht zu einem erstklassigen Bürger machen? – rotskoff

Antwort

17

Oft sind Definitionen in where Klauseln zu vermeiden, sich zu wiederholen, wenn ein Unterausdruck mehr als einmal in einer Definition vorkommt. In einem solchen Fall hält der Programmierer die lokale Definition für einen einfachen Stellvertreter zum Schreiben der Inline-Sub-Ausdrücke. Normalerweise würden Sie die Inline-Unterausdrücke nicht explizit eingeben, daher geben Sie auch nicht die where-Definition ein. Wenn Sie dies tun, um beim Tippen zu sparen, würde die Typdeklaration alle Ihre Ersparnisse zunichte machen.

Es scheint ziemlich üblich, Lerner von Haskell mit Beispielen dieser Form where einzuführen, so dass sie weiter denken, dass "normaler Stil" Typdeklarationen für lokale Definitionen nicht geben soll. Zumindest war es meine Erfahrung, Haskell zu lernen. Ich habe seither festgestellt, dass viele meiner Funktionen, die kompliziert genug sind, um einen where Block zu benötigen, ziemlich undurchschaubar werden, wenn ich den Typ der lokalen Definitionen nicht kenne, also versuche ich, sie immer jetzt zu tippen; Selbst wenn ich denke, dass der Typ offensichtlich ist, während ich den Code schreibe, ist es vielleicht nicht so offensichtlich, wenn ich es lese, nachdem ich es eine Zeit lang nicht angeschaut habe. Eine kleine Anstrengung für meine Finger wird fast immer durch ein oder zwei Fälle von typischem Rückschluss in meinem Kopf aufgewogen!

Ingo Antwort gibt einen guten Grund für bewusst keinen Typen auf eine lokale Definition zu geben, aber ich vermute, der Hauptgrund ist, dass viele Programmierer die Daumenregel, dass Typ assimiliert haben Erklärungen für Top-Level-Definitionen zur Verfügung gestellt werden, aber nicht für lokale Definitionen aus der Art, wie sie Haskell gelernt haben.

0

Durch das Hinzufügen einer Typsignatur kann der Code schneller werden. Nehmen Sie zum Beispiel das folgende Programm (Fibonacci):

result = fib 25 ; 
-- fib :: Int -> Int 
fib x = if x<2 then 1 else (fib (x-1)) + (fib (x-2)) 
  • Ohne die Anmerkung in der 2. Zeile, dauert es 0,010 Sekunden. zu rennen.
  • Mit der Int -> Int Annotation dauert es 0,002 Sekunden.

Dies geschieht, weil, wenn Sie über fib nichts sagen, es als fib :: (Num a, Num a1, Ord a) => a -> a1 eingegeben werden wird, was bedeutet, dass während der Laufzeit, zusätzliche Datenstrukturen („Wörterbücher“) werden zwischen den Funktionen werden müssen weitergegeben repräsentieren die Num/Ord Typklassen.

+1

Die Frage fragt nach Typensignaturen lokaler Definitionen. Es ist tatsächlich sehr üblich, den Top-Level-Definitionen Typensignaturen zu geben. – Vitus

+2

Ich stimme Vitus zu, diese Antwort ist für lokale Definitionen weniger relevant. Es ist sehr üblich, Definitionen auf oberster Ebene zu schreiben, denen ohne eine Typ-Signatur allgemeinere Typen zugewiesen werden, als Sie möglicherweise benötigt haben, und daher langsamer ausgeführt werden. Lokale Definitionen sind fast immer durch die Typensignatur der Definition der obersten Ebene, in der sie vorkommen, eingeschränkt. Dieses Problem tritt daher nicht so häufig auf, wenn Sie die Definitionen auf oberster Ebene eingeben. Selbst wenn die lokale Definition nicht formal eingeschränkt ist, weiß der Compiler, dass er lokal ist und nicht allgemeiner als seine lokale Verwendung sein muss. – Ben

11

Oft werden where Deklarationen für kurze, lokale Dinge verwendet, die einfache Typen oder einfach abzuleitende Typen haben. Daher hat der Mensch oder Compiler keinen Vorteil, den Typ hinzuzufügen.

Wenn der Typ komplex ist oder nicht abgeleitet werden kann, möchten Sie möglicherweise den Typ hinzufügen.

Während monomorphe Typ-Signaturen Funktionen auf höchster Ebene schneller ausführen können, ist es für lokale Definitionen in where -Klauseln kein so großer Gewinn, da GHC die Definitionen in den meisten Fällen sowieso inline einbaut und optimiert.

20

Es gibt eine Klasse von lokalen Funktionen, deren Typen in Haskell nicht geschrieben werden können (ohne die Verwendung von ausgefallenen GHC-Erweiterungen). Zum Beispiel:

f :: a -> (a, Int) 
f h = g 1 
    where g n = (h, n) 

Dies liegt daran, während die a im f Art Signatur von außen f polymorphen betrachtet wird, diese von innen f nicht so ist. In g ist es nur etwas unbekannter Typ, aber nicht beliebig Typ, und (Standard) Haskell kann nicht "den gleichen Typ wie das erste Argument der Funktion, in der dieser definiert ist" in seiner Typsprache ausdrücken.

+4

Obwohl '{- # LANGUAGE ScopedTypeVariables # -}' erlaubt Ihnen, den Typ 'g :: Int -> (a, Int)' zu geben, wenn Sie die Typ-Signatur für 'f' ändern, um' forall a zu sein. a -> (a, Int) '. –