2014-10-25 7 views
13

Einfache Frage: Warum löst das nicht die Rewrite-Regel aus?Wie fmp rewrite Regeln auslösen?

{-# RULES "fmap/fmap" forall f g xs. fmap f (fmap g xs) = fmap (f.g) xs #-} 

main = do 
    txt <- fmap head (fmap words (readFile "foo.txt")) 
    print txt 

Jetzt wollte ich, dass fun die Regel löst das Extrahieren schreiben, weil es in einem früheren Test tat ... diesmal nicht.

{-# RULES "fmap/fmap" forall f g xs. fmap f (fmap g xs) = fmap (f.g) xs #-} 

fun f g xs = fmap f (fmap g xs) 

main = do 
    txt <- fun (drop 1) words (readFile "foo.txt") 
    print txt 

Bis ich zufällig hat einen Modulname:

module Main where 

{-# RULES "fmap/fmap" forall f g xs. fmap f (fmap g xs) = fmap (f.g) xs #-} 

fun f g xs = fmap f (fmap g xs) 

main = do 
    txt <- fun head words (readFile "foo.txt") 
    print txt 

Jetzt noch funktioniert es nicht, wenn ich in der Hauptfunktion die Funktion Anwendung schreiben gerade.

Um es zusammenzufassen:

  • txt <- fmap head (fmap words (readFile "foo")) nicht
  • funktioniert txt <- fun head words (readFile "foo") funktioniert nicht
  • txt <- fun head words (readFile "foo")plus Modul
  • fun f g xs = fmap f . fmap g $ xsarbeitet plus Modul funktioniert nicht
  • fun f g xs = f <$> (g <$> xs)plus Modul funktioniert (aber später ausgelöst)

All dies durch den Aufruf ghc --make -O2 -ddump-rule-firings Main.hs gemacht wurde. Beispielausgabe:

# ghc --make -O2 -ddump-rule-firings Main.hs 
[1 of 1] Compiling Main    (Main.hs, Main.o) 
Rule fired: fmap/fmap 
Rule fired: unpack 
Rule fired: Class op >>= 
Rule fired: Class op fmap 
Rule fired: Class op fmap 
Rule fired: Class op show 
Rule fired: Class op showList 
Rule fired: unpack-list 
Linking Main ... 
+3

Nur als eine Nebenfrage: Weiß jemand, was diese 'Class op' Regeln sind? Nach ihnen zu suchen, konnte ich nicht herausfinden. Abgesehen davon: 'rewrite-rules' tag? Scheint nicht üblich genug, um seine Schöpfung zu rechtfertigen. – fho

+3

Die 'Class op'-Regeln werden von GHC für monomorphes Auftreten von überladenen Funktionen erstellt. Siehe 'compiler/basicTypes/MkId.lhs' in GHC (suche nach' "Class op" '): _Dies ist die eingebaute Regel, die' op (dfT d1 d2) ---> opT d1 d2'_ – Cactus

Antwort

13

Anbetracht dessen, was @Cactus sagte, was ich glaube, hier ist passiert, dass die Regel Class op fmapersetzt Ihre fmap mit seinen method definition for IO:

instance Functor IO where 
    fmap f x = x >>= (pure . f) 

Wenn dies überall geschieht, bevor die Regel wird ausgelöst, dann wird fmap nicht mehr in (GHC's interner Darstellung von) Ihrem Code für Ihre eigene Regel übrig sein, um an zu triggern.

GHC versucht Klassenmethoden zu spezialisieren, wenn sie auf bestimmte Arten verwendet werden, so dass, wenn die Monade fmap innerhalb vollständig bekannt ist, verwendet wird, gibt wird keine allgemeine fmap gelassen, sobald es mit Spezialisierung erfolgt.

Also die verbleibende Frage ist, warum tut Ihre Regel Feuer, wenn Sie einen Modul-Header bereitstellen?

module Main where 

Die Antwort liegt darin, wie dies unterscheidet sich geringfügig von der Standard Modul-Header, der verwendet wird, wenn Sie eine nicht bieten:

module Main (main) where 

Beachten Sie, dass diese exportiert ausdrücklich nurmain aus dem Modul.Ihre Version ohne Exportliste exportiert stattdessen alles, was im Modul main und fun definiert ist.

Wenn nur main exportiert wird, kann GHC ableiten, dass fun nur intern in main verwendet wird, und es vollständig dort inline, kein Standalone-Version machen stört. Dann stellt es fest, dass die fmap s nur für IO verwendet werden, und spezialisiert sich auf sie. Oder möglicherweise tut es es in der umgekehrten Reihenfolge, aber das Endergebnis ist das gleiche. Wenn fun ebenfalls exportiert wird, muss GHC davon ausgehen, dass die Benutzer Ihres Moduls es in einer beliebigen Monade aufrufen möchten. Daher kompiliert GHC dann eine eigenständige Version von fun für eine generische Monade, die hält generischen, und Ihre Regel ist in der Lage, auf diese Version zu feuern.

Aber auch für den expliziten module Code, die Class op fmap Regel feuert zweimal beim Kompilieren, als ob es zwei getrennten fmap s angewandt wird. Ich vermute daher, dass auch in diesem Fall ist fun inlined und spezialisierte sich auf mainvor die Regel es nur eine fmap, verwenden vereinfacht so die inlined Version innerhalb main verwendet noch nicht gehabt haben Ihre Regel angewendet.

+0

Ah Verdammt ... also mein Beispiel war irgendwie pathologisch ... im echten Code sind die Daten nur eine Instanz von 'Functor' und sonst nichts. Große Antwort übrigens. Klar und prägnant. – fho

+0

Nur FYI, der Link ist tot (404). – ThreeFx

+1

@ThreeFx Gah, die Dokumentationsverknüpfungen von GHC sind nicht für die Dauerhaftigkeit optimiert ... oder für das Finden einer permanenten Verbindung, wenn Sie eine wünschen. Ich habe mich auf einen hoffentlich permanenten Link zur aktuellen Version geupdated und es geschafft, gleichzeitig einen richtigen Anker zu bekommen. –