Ich versuche eine Spielengine zu erstellen, die aus ein paar verschiedenen Typen besteht:
%Vor% Ich versuche jedoch, grundlegende Physik für alle diese Entitäten zu implementieren. Dies erfordert, dass sie jeweils ein pos :: (Double, Double, Double)
und ein velocity :: (Double, Double, Double)
enthalten.
In objektorientierten Sprachen würde ich es wie folgt implementieren:
%Vor% wobei PhysicalObject
die zwei Eigenschaften pos
und velocity
enthält.
Meine unmittelbare Reaktion war, sie alle auf den gleichen Typ zu setzen:
%Vor% Allerdings befürchte ich, dass dies die Implementierung von kameraspezifischen Funktionen, lichtspezifischen Funktionen usw. erschweren könnte. In Wirklichkeit haben sie nur noch sehr wenig gemeinsam, abgesehen davon, dass sie alle eine physische Position und Geschwindigkeit besitzen in der Welt.Gibt es eine einfachere Möglichkeit, dies zu tun, als die beiden Eigenschaften in jedem Typkonstruktor zu definieren?
Ich kann mir zwei Ansätze vorstellen - Klassen und Linsen.
Sie würden dann Instanzen für die Objekte entlang der folgenden Zeilen erstellen
%Vor% und ähnlich für Ihre anderen Arten. Dann kann jede Funktion, die die Details eines Objekts nicht kennen muss, nur ihre Argumente als Instanzen von PhysicalObject
angeben, zum Beispiel:
Sie werden jedoch Schwierigkeiten haben, Funktionen zu schreiben, die Ihre Objekte mit diesem Code modifizieren - die Klasse teilt Haskell mit, wie sie auf die Position und Geschwindigkeit eines Objekts zugreifen kann, aber nicht wie sie geändert werden / p>
Die andere Möglichkeit besteht darin, sich an die Objektiv -Bibliothek zu wenden. Dies ist ein bisschen tierisch, aber es erlaubt Ihnen, einen sehr natürlichen Code zu schreiben. Zuerst gibt es ein bisschen Boilerplate
%Vor%Definieren Sie nun einige Positions- und Geschwindigkeitsdatentypen. Mach dir keine Sorgen über die seltsamen Feldnamen, die mit Unterstrichen versehen sind - wir werden sie nicht verwenden.
%Vor% Nun verwenden Sie ein wenig Template Haskell, um Objektive für Ihre Datentypen abzuleiten. Dadurch werden die Typklassen HasPos
und HasVel
generiert, deren Methoden Ihnen erlauben, auf alle Werte zuzugreifen und diese zu ändern, die eine Instanz dieser Klassen sind.
Definieren Sie nun Ihre Kamera-Klasse, die eine Position und eine Geschwindigkeit enthält.
%Vor% Ein weiteres Bit von Template Haskell erstellt automatisch die Funktionen cameraPos
und cameraVel
, mit denen Sie auf die Position und Geschwindigkeit Ihrer Kamera zugreifen und diese ändern können.
Erklären Sie abschließend, dass Ihre Kamera eine Instanz der Klassen HasPos
und HasVel
ist, mit einer Standardimplementierung ihrer Methoden.
Jetzt sind wir bereit, echte Arbeit zu leisten. Lassen Sie uns eine Beispielkamera definieren
%Vor%Eine Funktion zum Ändern der Kamera, die eine neue mit einer aktualisierten Position zurückgibt, ist
%Vor% Beachten Sie, dass dies eine vollständig generische Funktion zum Verschieben eines Objekts mit einer Position und Geschwindigkeit ist - es ist überhaupt nicht spezifisch für den Camera
-Typ. Es hat auch den Vorteil, viel wie Imperativ-Code zu sehen!
Wenn Sie nun all dies in GHCI laden, können Sie es in Aktion sehen
%Vor%Sie wollen anfangen, von Dingen wie Typklassen und Objektkodierungen abhängig zu sein. Die erste Methode besteht darin, die allgemeine Schnittstelle als eine Typklasse zu codieren, von der jeder Typ erbt.
%Vor%Die zweite besteht darin, ein gemeinsames Objekt zu erstellen
%Vor%könnte sogar verwendet werden, um die erste Typklasse zu instanziieren
%Vor%Seien Sie bei Typenklassen-Kodierungen jedoch vorsichtig, da ein zu großer Einsatz von ihnen oft zu Mehrdeutigkeiten beim Lesen von Code führt. Es kann schwierig sein, die Typen zu verstehen und zu wissen, welche Instanz verwendet wird.