2016-04-28 12 views
1

Ich habe ein Problem. Ich habe einen Datentyp Thing, der im Wesentlichen eine große Aufzeichnung von Maybe Doubles oder Maybe Texts ist. Ich möchte einen Datentyp erstellen, der die Idee der Ausführung von Berechnungen erfassen kann, obwohl einige Felder nichts sein können.Haskell: Applicative über Tupel

data Computation a b = Computation 
    { getInputs :: Thing -> a 
     computation :: a -> b 
     modifyThing :: b -> Thing -> Thing 
    } 

Basierend auf dem Ausgang b, die am ehesten zu einem Feld in der Sache entsprechen werden, möchte ich eine neue Sache zu schaffen.

modifyThing :: b -> Thing -> Thing 

Die Berechnung von Thing Vielleicht in zwei Teile aufgeteilt werden, b können die Variablen und eine Berechnung laden, die nur Zahlen oder Texte akzeptiert.

getInputs :: Thing -> a 

computation :: a -> b 

Das obige ist fast das, was ich will. In diesem Fall wäre a (vielleicht a1, vielleicht a2 ..) und so weiter. Dies bedeutet, dass in „Berechnung“ und getInputs ich so etwas wie

getInputs t = \t -> (getProp1 t , getProp2 t) 

computation = \(m_a1, m_a2) -> do 
    a1 <- m_a1 
    a2 <- m_a2 
    return $ a1 + a2 

zu tun haben, würde ich viel eher wie

suchen haben Rechen- und dann wie etwas tun

runComputation :: Thing -> Computation -> b 
runComputation thing comp = magic (computation comp) ((getInputs comp) thing) 
    where magic = ??? 

Das Problem ist, dass ich nicht weiß, wie man von

geht

zu

a1 -> a2 -> ... -> a_n 

Wenn eine der vielleicht ist nichts anderes als nur nichts zurück. Ich kann

pure computation <*> m_a1 <*> m_a2 <*> m_a3 

aber wie kann ich schreiben, um für jede Art von Tupel zu arbeiten?

P.S.

Ich dachte über die Berechnung als

computation :: Thing -> b 

schreiben und tun weg mit getInputs, aber es scheint, wäre es viel unweildy für mich mit zu testen und zu spielen. Deshalb versuche ich, mit dem oben beschriebenen Ansatz zu gehen. Denkst du, dass das eine gute Idee ist, was ich getan habe?

bearbeitet

Während keine Lösung für die jeweilige Frage, die ich gefragt, sondern auf die Absicht von dem, was ich tun wollte, ich, dass

data Computation = Computation 
    { getInputs :: Thing -> Maybe a 
    , computation :: a -> b 
    , modifyThing :: b -> Thing -> Thing 
    } 
tun beschlossen

der beste Weg wäre, vorwärts gehen. Auf diese Weise muss ich mich nicht um die Tupel kümmern.

+0

Gibt es einen Grund, warum Sie nicht eine Liste von vielleicht ist nicht verwenden kann? – Guvante

+0

@ Guvante Ein Nachteil von Listen ist, dass sie homogen sind - z. man wäre nicht in der Lage, gleichzeitig ein "Double" -y typisiertes Feld und ein "Int" -y typisiertes Feld auszuwählen. –

+0

@ user3550758 Wenn Sie eine Lösung finden, ist der StackOverflow-Weg, um es als Antwort zu schreiben, nicht in eine Bearbeitung der Frage enthalten. Ich ermutige Sie, Ihre "Bearbeitung" in eine eigene Einheit aufzuteilen - und akzeptieren Sie sie sogar, wenn es die beste Antwort ist, die Sie erhalten. (Dies wird hier als vollkommen höflich und sogar wünschenswert angesehen.) –

Antwort

0

Ich bin nicht ganz sicher, was der Vorteil der Verwendung einer Computation anstelle einer Thing -> Thing, aber Sie sollten in der Lage, die fraglichen Funktionen ziemlich einfach zu definieren. Zunächst einmal kann Ihre Beispielfunktion computation in einer etwas bequemeren Form umgeschrieben werden.(Sie sollten es eigentlich etwas anderes nennen, da der Name computation bereits von Ihrem Record Accessor belegt ist).

exampleComp :: Num a => (Maybe a, Maybe a) -> Maybe a 
exampleComp (a1, a2) = liftA2 (+) a1 a2 

Es ist immer noch nicht ganz so bequem wie einfache Addition, aber es kommt nahe. Ihre runComputation Funktion ist auch ziemlich einfach, da es wirklich nur darauf ankommt, die drei Komponenten Ihres Computation Typs zu komponieren.

runComputation :: Thing -> Computation -> Thing 
runComputation thing (Computation ins comp modify) = modify (comp $ ins thing) thing 

Oder mit Record Accessoren geschrieben, sieht es so aus.

runComputation thing comp = modifyThing comp 
          (computation comp $ getInputs comp thing) 
          thing 

Grundsätzlich ist diese Funktion extrahiert die geeigneten Daten getInputs Verwendung gibt diese Daten an computation einen neuen Satz von Daten zu erhalten, dann geht die dass zu modifyThing um tatsächlich das Objekt zu modifizieren.

Ohne weitere Informationen über genau was Sie mit Ihrem Datentyp zu tun planen, ist es schwer zu sagen, welche Methode Sie verwenden sollten. Persönlich kann ich keinen Grund sehen, computation und modifyThing zu trennen, aber ich kann wirklich keinen Grund sehen, Computation an erster Stelle entweder zu verwenden. Egal, das ist mehr eine Frage für CodeReview als für StackOverflow.

1

Das Problem ist, dass ich weiß nicht, wie von

(Maybe a1, Maybe a2, ... , Maybe a_n) 

zu

a1 -> a2 -> ... -> a_n 

ähnlich-ish zu uncurry Looks gehen, die eine normale Funktion übernimmt und erstellt eine Version, die statt mehrerer Argumente ein Tupel verwendet.

uncurry :: (a -> b -> c) -> (a, b) -> c 
uncurry f (a, b) = f a b 

Dieses spezielle Beispiel:

computation = \(m_a1, m_a2) -> do 
    a1 <- m_a1 
    a2 <- m_a2 
    return $ a1 + a2 

mit Applicative wie so

computation (m_a1, m_a2) = (+) <$> m_a1 <*> m_a2 

Aber man abstrahieren kann dies wie uncurry so zu etwas getan werden:

uncurryA :: Applicative f => (a -> b -> c) -> (f a, f b) -> f c 
uncurryA f (a, b) = f <$> a <*> b 

computation Zulassen wie so definiert werden:

computation a_b = uncurryA (+) a_b 
+0

'uncurry' funktioniert jedoch nur mit 2-Argument-Funktionen. Sie könnten eine Art rekursives Uncurrying durchführen (da 'uncurry. Uncurry' zum Beispiel' a -> a -> a -> b 'in '((a, a), a) -> b') verwandeln kann, aber Sie müssten immer noch herausfinden, wie Sie das resultierende verschachtelte Tupel reduzieren können. – chepner

+0

(Oder genauer, "uncurry" behandelt nur die ersten beiden Argumente gleichzeitig, was zu dem verschachtelten Tupel führt, wenn "uncurry" mehrmals nacheinander verwendet wird.) – chepner