2016-12-19 1 views
1

Ich teste einige Optimierungstechniken für das Rendern statischer Voxelszenen und lerne etwas Haskell auf dem Weg. Ich habe Voxel definiert alsHaskell entspricht der Klassenspezialisierung in OOP

type Voxel = (VoxelId, Position). 

wo VoxelId einfach für Int ein Alias ​​ist. Da ich eine lineare Algebra entlang der Linie antizipiere, wollte ich Position 3-dimensionalen Vektor darstellen. Bibliothek, die ich verwende (matrix) implementiert nur Matrix Datentyp, aber mit Position haben zugrunde liegenden Typ von Matrix nur vereinfachen weitere Berechnungen, damit ich damit einverstanden bin.

Allerdings ist es nicht viel Sinn für Position anders als 3-dimensionale Vektor etwas zu sein, so würde Ich mag Position zu 3x1 Matrizen beschränken, so dass, wenn ich speziell Position in einer Funktion Signatur verwenden, I‘ m garantiert entweder 3x1 Matrix oder Kompilierungsfehler.

In C++ oder andere OO Sprache, die ich so etwas tun könnte:

class Matrix 
{ 
    Matrix(int rows, int columns) 
    {} 
} 

class Position : public Matrix 
{ 
    Position() 
    : Matrix (3, 1) 
    {} 
} 

und Position verwenden, wenn ich nur 3x1 Matrix und Referenz erwarte überall sonst auf Matrix.

die Matrix aus der Bibliothek übernehmen Lassen ich bin mit einer Konstruktorfunktion

matrix :: (rows) -> (columns) -> Matrix 

Wie kann ich dieses Verhalten in Haskell replizieren, ohne Matrix Geräte für Position alle Operationen neu zu definieren?

+1

Es macht wenig Sinn, einen unveränderlichen, aber leeren Vektor in Haskell zu konstruieren. Was Sie tun können, ist Ihren eigenen "intelligenten Konstruktor" (eine Grundfunktion) zu definieren, der nur z.B. '(Double, Double, Double)' und gibt die Matrix zurück. Wenn Sie mehr statische Garantien haben möchten, fügen Sie die resultierende Matrix in einen "newtype" -Wrapper ein (beachten Sie, dass dazu auch alle Matrixoperationen auf dem Wrapper definiert oder jedes Mal neu entpackt werden müssen). – chi

+0

Die Neudefinition von Matrix-Operationen für Position widerlegt den Zweck der Verwendung von Drittanbieter-Bibliotheken und es ist etwas, das ich als letzte Lösung verlasse. Ich habe die ursprüngliche Frage bearbeitet, um sie klarer zu machen. –

+0

Dann müssen Sie jedes Mal manuell auspacken oder sichere Zwänge verwenden. Oder benutze einen dümmer Konstruktor wie ich unten zeige. – chi

Antwort

1

In Haskell gibt es keine Untertypen wie in OOP.

Sie können Sie eigene Datentyp definieren

newtype Position = P { unP :: Matrix ... } 
    deriving (Show) -- , etc. 

und eine Smart-Konstruktor

position :: Double -> Double -> Double -> Position 
position x1 x2 x3 = 
    Position (newMatrix 1 3 [[x1,x2,x3]]) -- pseudo code 

Wenn Sie wirklich die interne Darstellung ausblenden möchten, können Sie die oben in einem eigenen Modul setzen und Export nur der schlaue Konstruktor. Dazu müssen Sie jedoch alle Operationen definieren und exportieren, die Sie für diesen Position-Typ benötigen. Anderenfalls ist es zu undurchsichtig, um nützlich zu sein.

Angenommen, Sie verbergen die Darstellung nicht, beachten Sie, dass Position und Matrix ... zwei verschiedene Typen sind. Sie können also keine Position an etwas übergeben, das eine Matrix möchte. Anstatt also

matrixMultiply somePosition someMatrix 

braucht

matrixMultiply (unP somePosition) someMatrix 

und ein Matrix Ergebnis zu tun. Wenn das Ergebnis ein Position stattdessen sein sollte, kann man/braucht eine individuelle Multiplikationsfunktion zu definieren:

posMultiply :: Position -> Matrix ... -> Position 
posMultiply m _ | wrongSize m = error "matrix has the wrong size!" 
posMultiply m (Position p) = Position (matrixMultiply m p) 

Je nachdem, wie viel statische garantiert Sie wollen, das ein bisschen umständlich bekommen kann. In vielen Fällen kann safe coercions den Schmerz erweichen.

Eine einfachere Alternative könnte ein Dümmerkonstruktor sein, der keinen benutzerdefinierten Typ verwendet. Dies ist eine einfache Funktion

position :: Double -> Double -> Double -> Matrix ... 
position x1 x2 x3 = 
    newMatrix 1 3 [[x1,x2,x3]] -- pseudo code 
+0

Danke für die Antwort, ich denke, ich habe eine Idee, wie ich mit diesem Problem jetzt umgehen soll, obwohl ich zugeben muss, dass ich kein großer Fan von irgendeiner dieser Lösungen bin (sie sind definitiv besser als alles, was mir dazu einfällt). Ich erinnere mich an einen Vortrag von Simon Peyton über Kompromisse von Typ-System in Haskell, wo er etwas sagte in Richtung: "in Haskell ist es einfach, neue Funktionalität zu bestehenden Typ hinzuzufügen, während in Imperativ-Sprachen ist es einfach, neue zu geben type existing functionment, aber in beiden Fällen wird inverse wirklich chaotisch "und ich nehme an, dass dies der Fall ist, der zeigt, wie wahr diese Aussage ist. –

+0

@RverentLapwing Ja, ich stimme zu. Dies wird als ["Ausdrucksproblem"] (https://en.wikipedia.org/wiki/Expression_problem) bezeichnet. – chi