2010-01-19 4 views
7

Gibt es sowieso Implementierung von Resource Acquisition ist Initialisierung in Scheme?RAII in Schema?

Ich weiß, dass RAII nicht gut in GC-Ed-Sprachen funktioniert (da wir keine Ahnung haben, ob das Objekt zerstört wird). Scheme hat jedoch schöne Dinge wie Fortsetzungen, dynamischen Wind und Schließungen - gibt es eine Möglichkeit, eine Kombination davon zu verwenden, um RAII zu implementieren?

Wenn nicht, wie entwerfen Intriganten ihren Code, um RAII nicht zu verwenden?

[Ein gängiges Beispiel ich in laufen ist die folgende:

ich ein 3D-Netz, ich ein Vertex Buffer Object atached es haben, wenn das Ineinander greifen nicht mehr verwendet wird, möchte ich die VBO freigegeben .]

Danke!

+0

Hallo, anon. Ich frage mich, ob meine Antwort Sie zufrieden gestellt hat oder ob Sie etwas anderes suchen. –

+0

Ich denke, Ihre Antwort ist so gut, wie es sein Schema gibt. Wir auf einer Ebene, müssen wir wissen, wenn das Modell "stirbt" und gibt es vbo. In RAII + GC müssen wir das jedoch nicht im Voraus wissen, wir können sagen: "Modell, ich weiß nicht, wann du sterben wirst, aber ich weiß, wenn du das tust, wirst du den VBO aufgeben ". Wir können das später nicht tun, weil Schema gc-ed ist; Worauf ich ursprünglich gehofft hatte ... war eine Art cleverer Makro-Maker, der automatisch eine Art von Ref-Zählen einschloss, die diese Art von RAII + Refcounting liefern würde. – anon

+0

Um dies weiter hinzuzufügen, betrachten Sie die folgende Situation: Wir erstellen ein Modell, wir wissen nicht, wann es gelöscht wird, aber wir wissen, dass es viel gerendert wurde; also geben wir ihm einen VBO; übergeben Sie es viel herum; ... und wenn niemand es benutzt, befreit es den VBO. Es gibt keinen einzigen Ort in dem Code, wo ich weiß "Ich kann jetzt das Modell befreien." – anon

Antwort

14

Ist dies nur ein einmaliger, könnten Sie schreiben immer nur ein Makro, das um dynamic-wind Wraps, in der vor dem Auf- und Abbau zu tun und nach Thunks (Ich gehe davon aus, dass allocate-vertex-buffer-object und free-vertex-buffer-object Konstruktor sind und Destruktoren hier):

(define-syntax with-vertex-buffer-object 
    (syntax-rules() 
    ((_ (name arg ...) body ...) 
    (let ((name #f)) 
     (dynamic-wind 
     (lambda() (set! name (allocate-vertex-buffer-object args ...))) 
     (lambda() body ...) 
     (lambda() (free-vertex-buffer-object name) (set! name #f))))))) 

Wenn dies ein Muster ist, dass Sie eine Menge zu verwenden, für verschiedene Arten von Objekten, könnten Sie ein Makro schreiben, diese Art von Makro zu erzeugen; und wahrscheinlich werden Sie eine Reihe von diesen auf einmal zuordnen wollen, so dass Sie vielleicht eine Liste von Bindungen am Anfang haben möchten, anstatt nur eine einzige.

Hier ist eine off-the-mansch, allgemeinere Version; Ich bin nicht wirklich sicher über den Namen, aber es zeigt die Grundidee (editiert Endlosschleife in Originalversion fix):

(define-syntax with-managed-objects 
    (syntax-rules() 
    ((_ ((name constructor destructor)) body ...) 
    (let ((name #f)) 
     (dynamic-wind 
     (lambda() (set! name constructor)) 
     (lambda() body ...) 
     (lambda() destructor (set! name #f))))) 
    ((_ ((name constructor destructor) rest ...) 
     body ...) 
    (with-managed-objects ((name constructor destructor)) 
     (with-managed-objects (rest ...) 
     body ...))) 
    ((_() body ...) 
    (begin body ...)))) 

Und Sie würden diese wie folgt verwenden:

(with-managed-objects ((vbo (allocate-vertex-buffer-object 1 2 3) 
          (free-vertext-buffer-object vbo)) 
         (frob (create-frobnozzle 'foo 'bar) 
          (destroy-frobnozzle frob))) 
    ;; do stuff ... 
) 

Hier ist ein Beispiel, das es funktioniert, einschließlich Verlassen und Wiedereintritt in den Anwendungsbereich Fortsetzungen verwenden (dies ist ein ziemlich konstruiertes Beispiel, Entschuldigung, wenn der Steuerablauf ist ein bisschen schwer zu folgen) zeigt:

(let ((inner-continuation #f)) 
    (if (with-managed-objects ((foo (begin (display "entering foo\n") 1) 
            (display "exiting foo\n")) 
          (bar (begin (display "entering bar\n") (+ foo 1)) 
            (display "exiting bar\n"))) 
     (display "inside\n") 
     (display "foo: ") (display foo) (newline) 
     (display "bar: ") (display bar) (newline) 
     (call/cc (lambda (inside) (set! inner-continuation inside) #t))) 
    (begin (display "* Let's try that again!\n") 
      (inner-continuation #f)) 
    (display "* All done\n"))) 

Dies sollte drucken:

 
entering foo 
entering bar 
inside 
foo: 1 
bar: 2 
exiting bar 
exiting foo 
* Let's try that again! 
entering foo 
entering bar 
exiting bar 
exiting foo 
* All done 

call/cc ist einfach eine Abkürzung für call-with-current-continuation; Verwenden Sie das längere Formular, wenn Ihr Schema nicht das kürzere Formular enthält.

Update: Wie Sie in Ihren Kommentaren geklärt haben, suchen Sie nach einer Möglichkeit, Ressourcen zu verwalten, die aus einem bestimmten dynamischen Kontext zurückgegeben werden können. In diesem Fall müssen Sie einen Finalizer verwenden. Ein Finalizer ist eine Funktion, die mit Ihrem Objekt aufgerufen wird, sobald der GC nachgewiesen hat, dass er von keinem anderen Ort aus erreicht werden kann. Finalizer sind nicht Standard, aber die meisten ausgereiften Scheme-Systeme haben sie, manchmal unter verschiedenen Namen. Zum Beispiel in PLT Scheme, siehe Wills and Executors.

Sie sollten daran denken, dass in Schema ein dynamischer Kontext erneut eingegeben werden kann; Dies unterscheidet sich von den meisten anderen Sprachen, in denen Sie möglicherweise mithilfe von Ausnahmen einen dynamischen Kontext an einem beliebigen Punkt beenden können. Sie können jedoch nicht erneut eingeben.In meinem obigen Beispiel demonstrierte ich einen naiven Ansatz, dynamic-wind zu verwenden, um Ressourcen freizugeben, wenn Sie den dynamischen Kontext verlassen, und sie neu zuzuweisen, wenn Sie erneut eingeben. Dies kann für einige Ressourcen geeignet sein, aber für viele Ressourcen wäre es nicht geeignet (z. B. das erneute Öffnen einer Datei, Sie befinden sich jetzt am Anfang der Datei, wenn Sie den dynamischen Kontext erneut eingeben) und haben möglicherweise erheblicher Aufwand.

Taylor Campbell (ja, es gibt eine Beziehung) hat an article in his blag (der Beitrag 2009-03-28) dieses Problem ansprechen und einige Alternativen basierend auf der genauen Semantik präsentieren, die Sie wollen. Zum Beispiel stellt er ein Formular unwind-protext zur Verfügung, das die Aufräumprozedur nicht aufruft, bis es nicht mehr möglich ist, den dynamischen Kontext wieder einzugeben, in dem die Ressource zugänglich ist.

Also, das deckt eine Menge verschiedener Optionen ab, die verfügbar sind. Es gibt keine exakte Übereinstimmung mit RAII, da Schema eine sehr unterschiedliche Sprache ist und sehr unterschiedliche Einschränkungen hat. Wenn Sie einen spezifischeren Anwendungsfall oder mehr Details zu dem Anwendungsfall haben, den Sie kurz erwähnt haben, kann ich Ihnen möglicherweise einige spezifischere Ratschläge geben.