2016-07-21 11 views
1

Ich bin ziemlich sicher, dass dies eine einfache Lösung hat, aber es entzieht sich mir und ich kann nicht scheinen, eine klare Antwort zu finden.Inverse Aufhebung auf anwendungsspezifischen Funktoren

Normalerweise, wenn liftA2 unter der Annahme der binären Funktion Anwendung bereits einmal angehoben worden ist, sieht die Signatur wie folgt aus:

liftA2' :: (Applicative f1, Applicative f) 
      => (f a -> f b -> f c) -> f1 (f a) -> f1 (f b) -> f1 (f c) 

Ist es möglich, die „inverse“ von zum Beispiel liftA2 wie anzuwenden:

inverseA2 :: (Applicative f, Applicative f1) 
      => (f a -> f b -> f c) -> f (f1 a) -> f (f1 b) -> f (f1 c) 

Als ein konkretes Beispiel, würde Ich mag die Funktion erhalten:

f :: ([a] -> [b] -> [c]) -> [Maybe a] -> [Maybe b] -> [Maybe c] 

Ein Weg, um „Pack“ jedes Argument [Maybe a] -> Maybe [a] und „auspacken“ Maybe [a] -> [Maybe a] das Ergebnis der Anwendung eines normalen liftA2 zu greifen wäre. Ich möchte das vermeiden, da, wie Sie sich vorstellen können, die Verpackung destruktiv ist (z. B. pack [Just 1, Nothing, Just 2] == Nothing).


Update: wie @ user2407038 wies darauf hin, für f, um die gegebene Funktion, die Sie unbedingt benötigen eine Funktion entlang der Linien von [Maybe a] -> [a] anzuwenden, die tut Informationen zu verlieren. Für diese beiden speziellen Funktoren gibt es keine offensichtliche Möglichkeit, die zusätzliche Anforderung zu erfüllen. Aber für alle anderen zwei Funktoren f, f1, die eine invertierbare Funktion haben forall a . f a -> f1 a die Antwort akzeptiert passt perfekt als eine Lösung für diese Frage.

+0

Nehmen Sie 'f :: [Int] -> [Char] -> [Int]; f x y = [Länge x + Länge y] '. Was würdest du von 'inverseA2 f :: [Maybe Int] -> [Vielleicht Char] -> [Maybe Int] 'erwarten? Was ist mit 'f x y = [Länge x + Länge y, Kopf x]'? Ich bin nicht sicher, was die allgemeine Definition wäre ... – chi

+2

Damit 'f' die gegebene Funktion anwenden kann, muss es ein' [a] 'aus einem' [Maybe a] 'erzeugen (natürlich gibt es keinen Weg um '[a]' von '[Maybe b]' zu bekommen, da es keine Möglichkeit gibt, 'a' von' b' zu bekommen und somit * unbedingt * eine Funktion '[Maybe a] -> [a]' - und wie Sie bemerkt haben, verliert diese Funktion notwendigerweise Informationen. Dasselbe gilt für den allgemeinen Fall - es gibt wenige Funktoren 'f1, f2', die eine invertierbare Funktion' forall a 'haben. f1 a -> f2 a'. – user2407038

+0

@ user2407038 also gab es einen Grund, warum ich keine einfache Antwort gefunden hatte, da ich das falsche Problem betrachtete. Danke, dass du auf die invertierbaren Funktionen hingewiesen hast! Ich werde mehr darüber nachdenken. –

Antwort

1

Ich bin sicher, dass Sie das wahrscheinlich herausgefunden haben, aber ich denke nicht, dass Sie dies mit den Einschränkungen, die Sie haben, tun können. Wenn Sie ein bisschen mehr liberal mit Einschränkungen sind, erhalten Sie etwas obwohl ....

inverseA2 :: (Applicative f, Traversable f, Applicative f1, Traversable f1) 
       => (f a -> f b -> f c) -> f (f1 a) -> f (f1 b) -> f (f1 c) 
inverseA2 f x y = sequenceA (liftA2 f (sequenceA x) (sequenceA y)) 

Der einzige Grund, warum ich dieses aufstellen bin ist, dass für Ihr spezielles Beispiel mit Maybe und [], diese Einschränkungen sind alle zufrieden, so dass dies für diesen Fall möglich ist. Dennoch beruhigt sich das überhaupt nicht.

Sie auch Ihre eigenen Instanzen für Data.Distributive geben Sie distribute mit dem Schreiben zu experimentieren versuchen könnte, die sequenceA ähnlich ist ...

Edited enthalten @ dfeuer Vorschläge.

+0

Sehr elegant! 'sequence' beschreibt perfekt was' pack' und 'unpack' tut, danke für das Aufzeigen (jetzt merke ich, dass ich Kauderwelsch spreche, wenn ich über das Transponieren spreche, ich werde das korrigieren)! Leider, wie Sie gesagt haben, implementiert es den Fall, den ich vermeiden möchte. Wie im obigen Beispiel wird bei der Auswertung von inversM2 (\ xy = [Länge x + Länge y]) [Nichts, Nur 1, Nur 3] [Nur 'a', Nur 'b'] 'stattdessen '[Nichts]' zurückgegeben von '[Just 5]' –

+2

'Monad' ist eindeutig übertrieben (ersetzen Sie' sequence' durch 'sequenceA'). Außerdem würde ich spekulieren, dass Sie mehrere Kombinationen von 'Traversable' und' Distributive' für den Kontext verwenden könnten. – dfeuer

+0

@dfeuer Danke! Ich vergesse ständig über 'sequenceA' ... – Alec