2015-10-21 3 views
14

Ich habe eine Funktion (*~). Der größte Teil der Kosten für die x *~ y Auswertung kommt aus dem zweiten Argument Inspektion, etwa entlang dieser Linien:Teilweise Auswertung rechtshändige Bedienerabschnitte

(*~) :: a -> b d -> c d a 
x *~ y = case y' of 
      Bar -> cheapFunction y' x 
      Baz -> cheapFunction2 y' x 
      Quux -> cheapFunction3 y' x 
    where 
    y' = expensive y 

Gibt es eine Möglichkeit GHC zu überzeugen, teilweise Operator Abschnitte wie (*~ y) zu bewerten?

Ich habe versucht, es wie Umschreiben:

(*~) = flip go 
    where 
    go y = let y' = expensive y 
      in case y' of 
       Bar -> cheapFunction y' 
       Baz -> cheapFunction2 y' 
       Quux -> cheapFunction3 y' 

aber es schien nicht zu helfen. Ich denke, dies könnte sein, weil flip alle Argumente benötigt, bevor es das Spiegeln macht?

Ein Weg wäre nur, den Operator selbst umzudrehen, aber er liest viel natürlicher, wenn der teure Operand auf der rechten Seite steht, weil er mit einer existierenden Notation übereinstimmt.

Kann ein richtig gefertigter {-# RULE #-} mich hier heraus retten? Wenn ja, was sollte es sagen? (Ich bin unklar, wie weit die Schnittsyntax entziffert wurde, bevor Regeln nach Übereinstimmungen suchen.)

+1

Ich bin mir nicht sicher, welche Teilbewertung Sie kaufen würde. 'y'' wird bereits geteilt. Möchten Sie Memoization? Sie müssen Memoization selbst hinzufügen. Welche Art von Regel möchten Sie schreiben? –

+1

Was passiert, wenn Sie einen benutzerdefinierten 'Flip' verwenden? 'flip 'f x = \ y -> inline f y x' – dfeuer

+2

@ReinHenrichs Wird' y'' schon geteilt? Wenn ja, dann ist mein Verständnis ziemlich kaputt und ich würde gerne eine Antwort akzeptieren, die erklärt, wie. Wenn ich 'fmap (* ~ y) someLongList' mache, wird es nicht jedes Mal neu berechnet? –

Antwort

5

Um eine solche Optimierung auszulösen, müssen Sie sicherstellen, dass Ihre Funktion inline wird. Platzieren Sie das Pragma {-# INLINE (*~) #-} vor der Deklaration der (*~)-Funktion. Ich kann dir nicht garantieren, dass es dein Problem lösen wird, aber es ist die einzige Art, wie ich sehe, dass es angegangen wird. Ich überprüfe den generierten Core-Code anschließend mit einem Tool wie "ghc-core", um sicherzugehen.

Ihr Problem ist jedoch nur ein Hinweis auf eine falsche Codezusammensetzung. Ihre Funktion macht mehrere nicht zusammenhängende Dinge. expensive y sollte einfach davon ausgeschlossen werden, dann wird Ihr Problem als solches gelöscht. Das heißt, das Verwendungsmuster sollte x *~ expensive y anstelle von x *~ y sein.

+1

Das ist ein guter Punkt und würde das Problem lösen. Aber der Ergebnistyp von 'teur' ist derzeit ein verborgenes Implementierungsdetail, da es für Benutzer der Bibliothek, die' (* ~) 'exponieren, wirklich nicht von Interesse ist. –

+2

Eigentlich wäre es in Ihrem Fall auch kein verstecktes Implementierungsdetail, Sie würden es nur anders aufdecken. Sie würden auch Ihre Absicht verschleiern und diese vor dem Typsystem verbergen und müssten daher den Benutzer anderweitig darüber informieren, wie Ihre spezielle Funktion verwendet werden soll. Dies wäre vom Standpunkt der Benutzerfreundlichkeit aus möglicherweise nicht gut. Und wiederum würde es gegen die Trennung von Sorgen gehen. Sie haben eine Liste mit Hinweisen darauf, dass Ihr Ansatz bereits falsch ist. –

+0

Gute Punkte dort. –