F # Designmuster

8

Nehmen wir an, ich baue einen Parser für eine domänenspezifische Sprache in F #.

Ich habe eine diskriminierte Vereinigung definiert, um Ausdrücke darzustellen:

%Vor%

Nun habe ich einen AST vom Typ Expression erstellt und möchte Code dafür generieren. Ich habe eine Funktion, die Inferenz und Typprüfung für einen Ausdruck eingibt.

Es ist definiert wie

%Vor%

Und ich habe eine andere Funktion, um Code zu erzeugen, der einem ähnlichen Muster folgt: Nimm einen Ausdruck, schreibe Mustervergleichsanweisungen für jedes Element in der Vereinigung.

Meine Frage ist: Ist das der idiomatische Weg, es in F # zu machen?

Es scheint mir, dass es sauberer wäre, wenn jedes Mitglied der Gewerkschaft sein eigenes InferType und GenerateCode lokal damit definieren würde.

Wenn ich C # verwenden würde, würde ich eine abstrakte Basisklasse namens Expression mit virtuellen Methoden für InferType und GenerateCode definieren und sie dann in jeder Unterklasse überschreiben.

Gibt es einen anderen Weg, dies zu tun?

    
HS. 20.12.2009, 17:57
quelle

3 Antworten

17
  

Es scheint mir so zu sein   sauberer wenn jedes Mitglied der Gewerkschaft   definiert sein eigenes InferType und    GenerateCode lokal damit.

Ich glaube, Sie meinen "vertrauter", nicht "sauberer".

Wirklich, ist Ihr Idealfall, dass Sie Code-Implementierung in 10 verschiedenen Klassen verteilt?

Es besteht definitiv eine grundsätzliche Spannung zwischen der Frage, ob Sie Dinge "nach Art" oder "nach Operation" gruppieren wollen. Der übliche OO-Weg ist "nach Typ", während der FP-Weg (funktionale Programmierung) "nach Operation" ist.

Im Fall eines Compilers / Interpreters (oder der meisten Dinge, die sich in OO stark auf das Visitor-Muster beziehen) denke ich, dass "by operation" die natürlichere Gruppierung ist. Der Code-Generator für If und And und Or kann ein wenig gemeinsam haben; die Typenprüfer der verschiedenen Knoten werden in ähnlicher Weise Gemeinsamkeiten aufweisen; Wenn Sie einen Pretty-Printer erstellen, wird es wahrscheinlich Formatierungsroutinen geben, die allen Nice-Pretty-Printing-Implementierungen gemein sind. Im Gegensatz dazu haben das Drucken, die Typenkontrolle und das Codegennen von IfElse wirklich gar nichts miteinander zu tun. Warum sollten Sie diese also in einer Klasse IfElse gruppieren?

(Um deine Fragen zu beantworten: Ja, das ist idiomatisch. Gibt es einen anderen Weg - ja, du kannst es genauso machen wie in C #. Ich denke, du wirst feststellen, dass du viel weniger glücklich mit dem C # -Weg bist, und dass der Code auf diese Weise auch 2-3x größer ist, ohne Nutzen.

    
Brian 20.12.2009, 18:57
quelle
9

Als OCaml-Programmierer würde ich sagen, dass dies völlig idiomatisch ist. Übrigens gibt dies eine bessere Trennung von Belangen, als wenn Sie eine Klassenhierarchie mit Klassenmethoden geschrieben hätten. Sie würden eine ähnliche Modularität in einer OO-Sprache mit einem InferType-Besucher erhalten, aber es wäre viel mehr Code.

    
Tobu 20.12.2009 18:18
quelle
1

Die andere Sache, die Sie normalerweise in einer funktionalen Sprache tun könnten, besteht darin, eine fold -Operation für den Datentyp zu definieren und dann die typechecking- und code-erzeugenden Funktionen in Bezug auf die Faltung zu definieren. In diesem speziellen Fall bin ich mir nicht sicher, ob es dir viel einbringt, weil die Fold-Funktion so viele Argumente haben würde, dass es nicht besonders leicht zu verstehen sein wird:

%Vor%     
kvb 21.12.2009 13:57
quelle

Tags und Links