Wie kann ich ein gängiges rekursives Haskell-Fektormuster abstrahieren?

7

Bei der Verwendung von anwendungsorientierten Funktoren in Haskell bin ich oft in Situationen geraten, in denen ich mit repetitivem Code wie diesem gelandet bin:

%Vor%

In diesem Beispiel möchte ich sagen:

%Vor%

aber ich kann nicht herausfinden, wie applyMany (oder etwas Ähnliches) gemacht wird. Ich kann nicht einmal herausfinden, was der Typ wäre, aber es würde einen Datenkonstruktor benötigen, ein Int (n) , und eine Funktion, um n mal anzuwenden. Dies geschieht beim Erstellen von Instanzen für QuickCheck, SmallCheck, Data.Binary, Xml-Serialisierung und andere rekursive Situationen.

Also, wie könnte ich applyMany definieren?

    
Trystan Spangler 21.01.2011, 05:02
quelle

6 Antworten

6

Ich denke, Sie können es mit OverlappingInstances hack machen:

%Vor%     
Ed'ka 21.01.2011, 13:22
quelle
10

Auschecken Ableiten . Jede andere gute Generika-Bibliothek sollte dies auch können; Ableiten ist nur die, die ich kenne. Zum Beispiel:

%Vor%

Um auf die Frage einzugehen, die Sie eigentlich gestellt haben, hat FUZxxl recht, das ist in Plain-Vanilla-Haskell nicht möglich. Wie Sie feststellen, ist nicht klar, was sein Typ sein soll. Es ist möglich mit Template Haskell Metaprogrammierung (nicht allzu angenehm). Wenn Sie diesen Weg gehen, sollten Sie wahrscheinlich nur eine Generika-Bibliothek verwenden, die bereits die harte Forschung für Sie getan hat. Ich glaube, dass es auch möglich ist, Typenniveaus und Typklassen zu verwenden, aber unglücklicherweise sind solche Typenlösungen normalerweise schwierig zu abstrahieren. Conor McBride arbeitet an diesem Problem .

    
luqui 21.01.2011 05:25
quelle
5

Hier ist, was ich mindestens habe:

%Vor%

Anwendungsbeispiel:

%Vor%

Übrigens, ich denke, dass diese Lösung in der realen Welt nicht sehr nützlich ist. Vor allem ohne lokale Typklassen.

    
max taldykin 21.01.2011 14:55
quelle
5

Ich bin mit meiner anderen Antwort nicht zufrieden, ich habe mir einen Wahnsinnigen einfallen lassen.

%Vor% %Vor%

Langwierige Erklärung, wie ich das herausgefunden habe

Also hier ist, wie ich es bekommen habe. Ich fragte mich: "Nun, wie ist da schon eine Arbitrary -Instanz für (Int, Int, Int, Int) ? Ich bin mir sicher, niemand hat sie geschrieben, also muss sie irgendwie abgeleitet werden. Sicher genug, dass ich folgendes gefunden habe in den Dokumenten für Instanzen von Arbitrary :

%Vor%

Nun, wenn sie das schon definiert haben, warum dann nicht missbrauchen? Einfache Typen, die lediglich aus kleineren Arbiträrdatentypen bestehen, unterscheiden sich nicht wesentlich von einem Tupel.

Also muss ich nun irgendwie die "arbitrary" Methode für das 4-Tupel so umwandeln, dass es für meinen Typ funktioniert. Uncurrying ist wahrscheinlich beteiligt.

Stopp. Hoogle Zeit!

(Wir können leicht unser eigenes uncurry4 definieren, also nehmen wir an, dass wir das bereits haben.)

Ich habe einen Generator, arbitrary :: Gen (q,r,s,t) (wobei q, r, s, t alle Instanzen von Arbitrary sind). Aber sagen wir einfach, es ist arbitrary :: Gen a . Mit anderen Worten, a steht für (q,r,s,t) . Ich habe eine Funktion, uncurry4 , die den Typ (q -> r -> s -> t -> b) -> (q,r,s,t) -> b hat. Wir werden uncurry4 natürlich auf unseren Konstruktor SimpleType anwenden. Also uncurry4 SimpleType hat den Typ (q,r,s,t) -> SimpleType . Lassen Sie uns jedoch den Rückgabewert generisch halten, da Hoogle nichts über unseren SimpleType weiß. Wenn wir uns also an unsere Definition von a erinnern, haben wir im Wesentlichen uncurry4 SimpleType :: a -> b .

Ich habe also eine Gen a und eine Funktion a -> b . Und ich möchte ein Gen b Ergebnis. (Denken Sie daran, für unsere Situation ist a (q,r,s,t) und b ist SimpleType ). Ich suche also nach einer Funktion mit dieser Typsignatur: Gen a -> (a -> b) -> Gen b . Hoogling das , und Da Gen eine Instanz von Monad ist, erkenne ich sofort liftM als die monadisch-magische Lösung für meine Probleme.

Hoogle speichert den Tag erneut. Ich wusste, dass es wahrscheinlich einen "anhebenden" Kombinator gab, um das gewünschte Ergebnis zu erzielen, aber ich dachte ehrlich nicht daran, LiftM (durrr!) Zu benutzen, bis ich die Typensignatur hogelte.

    
Dan Burton 27.01.2011 04:55
quelle
4

Besuche lifeA2 und hebeA3 . Sie können auch Ihre eigenen applyTwice- oder applyThrice-Methoden schreiben:

%Vor%

Es gibt keinen einfachen Weg, den ich sehen kann, um das generische applyMany Sie verlangen, aber triviale Helfer wie diese zu schreiben ist weder schwierig noch ungewöhnlich.

[edit] Es stellt sich also heraus, dass so etwas funktioniert.

%Vor%

Aber es tut es nicht. (liftA4 MyType) hat eine Typensignatur von (Applicative f) => f Int -> f String -> f Double -> f Char -> f MyType . Dies ist nicht kompatibel mit dem ersten Parameter von quadraApply, der eine Typensignatur von (a -> a -> a -> a -> b) -> a -> b hat. Es würde nur für Datenstrukturen funktionieren, die mehrere Werte des gleichen beliebigen Typs enthalten.

%Vor%

Natürlich könntest du das einfach machen, wenn du diese Situation hättest

%Vor%

Es könnte ein Sprachpragma geben, das die "willkürliche" Funktion in die verschiedensten Datentypen einteilen kann. Aber das ist derzeit jenseits meines Niveaus von Haskell-fu.

    
Dan Burton 21.01.2011 06:50
quelle
2

Dies ist mit Haskell nicht möglich. Das Problem ist, dass Ihre Funktion einen Typ hat, der von dem numerischen Argument abhängt. Mit einem Typsystem, das abhängige Typen zulässt, sollte das möglich sein, aber ich denke nicht in Haskell.

Was Sie versuchen können, ist die Verwendung von Polymorphismus und Farbklassen, um dies zu erreichen, aber es könnte hacky werden und Sie brauchen eine große Menge von Erweiterungen, um den Compiler zu befriedigen.

    
fuz 21.01.2011 05:07
quelle