2014-07-23 17 views
8

ich wie dieser einen Plattentyp haben:Map Identität Funktors über Rekord

data VehicleState f = VehicleState 
         { 
         orientation :: f (Quaternion Double), 
         orientationRate :: f (Quaternion Double), 
         acceleration :: f (V3 (Acceleration Double)), 
         velocity :: f (V3 (Velocity Double)), 
         location :: f (Coordinate), 
         elapsedTime :: f (Time Double) 
         } 
        deriving (Show) 

Das ist cool, weil ich ein VehicleState Signal haben kann, wo ich alle Arten von Metadaten haben, kann ich eine VehicleState (Wire s e m()) haben, wo ich die netwire Semantik jedes Signals, oder ich kann eine VehicleState Identity haben, wo ich tatsächliche Werte zu einer bestimmten Zeit beobachtet habe.

Gibt es eine gute Möglichkeit, zwischen VehicleState Identity und VehicleState', definiert durch Zuordnung runIdentity über jedes Feld, hin und her zu mappen?

data VehicleState' = VehicleState' 
         { 
         orientation :: Quaternion Double, 
         orientationRate :: Quaternion Double, 
         acceleration :: V3 (Acceleration Double), 
         velocity :: V3 (Velocity Double), 
         location :: Coordinate, 
         elapsedTime :: Time Double 
         } 
        deriving (Show) 

Offensichtlich ist es trivial zu schreiben, aber ich habe tatsächlich einige Arten wie dies in meiner realen Anwendung und ich halte das Hinzufügen oder Entfernen von Feldern, so ist es langweilig.

Ich schreibe etwas Vorlage Haskell, die es tut, frage mich nur, ob ich das Rad neu erfinde.

+2

Sie könnten dies mit GHC Generics machen, aber ich weiß nicht, ob es einfacher wäre als TH. Da ich Erfahrung mit TH habe, wäre das auch meine Wahl. – bheklilr

+8

Was ich in Betracht ziehen würde: benutze 'VehicleState' überhaupt nicht und mache einen kurzen Alias ​​für' Identity' und 'runIdentity'. Ich weiß, nicht großartig. – luqui

+0

Oder verwenden Sie [newtype] (http://hackage.haskell.org/package/newtype) und seine Helfer. – luqui

Antwort

3

Wenn Sie nicht Familien Gegensatz zu geben und nicht zu viel Typinferenz benötigen, können Sie tatsächlich weg einen einzigen Datentyp mit der Verwendung:

import Data.Singletons.Prelude 

data Record f = Record 
    { x :: Apply f Int 
    , y :: Apply f Bool 
    , z :: Apply f String 
    } 

type Record' = Record IdSym0 

test1 :: Record (TyCon1 Maybe) 
test1 = Record (Just 3) Nothing (Just "foo") 

test2 :: Record' 
test2 = Record 2 False "bar" 

Die Apply Art Familie im singletons definiert Paket. Es kann auf verschiedene Art Funktionen auch in diesem Paket definiert angewendet werden (und natürlich können Sie Ihre eigenen definieren). Die IdSym0 hat die Eigenschaft, dass Apply IdSym0 x auf Ebene x reduziert wird. Und TyCon1 hat die Eigenschaft, Apply (TyCon1 f) x reduziert auf f x.

Wie von test1 und test2 gezeigt, erlaubt dies beide Versionen Ihres Datentyps. Sie benötigen jedoch jetzt Typ Anmerkungen für die meisten Datensätze.

+0

Sehr interessant, danke! –