Ich bin auf das gleiche Problem gestoßen: Ich habe eine Funktion wie
someFunc :: Int -> Int -> Int -> Int
Was ich gerne machen würde, ist eine magische Funktion zu erstellen, die zum Beispiel
listApply :: [Int] -> (Int -> Int -> Int -> Int) -> Int
so dass ich sagen kann
listApply [1,2,3] someFunc
Instinktiv scheint es, und Johns Antwort stimmt dem zu, dass es möglich sein sollte, eine Art Systemmagie einzusetzen, um dies zu erreichen. Es gibt Lösungen für ähnliche Probleme, die explizit iso-rekursive Datentypen mit einem Haufen expliziter Rolls und Unrolls beinhalten (siehe z.B. Kapitel 20 von Types and Programming Languages, oder den vierten Beitrag in dieses Thema ).
Ich habe eine Weile an der Type-Lösung herumgehackt; es fühlt sich möglich an, aber ich habe es nicht ganz hinbekommen, bevor ich mich entschied, Template Haskell auszuprobieren, und dort sind die Dinge viel freundlicher.
{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
lApply :: [Int] -> String -> ExpQ
lApply [] fn = return $ VarE (mkName fn)
lApply (l:ls) fn = [| $(lApply ls fn) l |]
(Denken Sie daran, das Pragma LANGUAGE oder den Befehlszeilenschalter -XTemplateHaskell zu verwenden).
Um dies zu verwenden, rufen Sie lApply innerhalb eines Splice wie folgt auf:
> $(lApply [1,2] "+")
3
Beachten Sie, dass ich eine Zeichenkette verwenden muss, die den Namen der Funktion enthält, die ich aufrufen möchte: Ich kann eine Funktion nicht direkt in eine ExpQ heben, aber ich kann auf ihre globale Bindung verweisen. Ich kann mir vorstellen, dass dies lästig werden könnte. Außerdem müssen die Argumente aufgrund der Art und Weise, wie wir die Liste durchlaufen, in umgekehrter Reihenfolge in der Liste dargestellt werden.
Es gibt noch ein paar andere Probleme: Um dies auf andere Datentypen zu verallgemeinern, müssen diese Typen entsprechende Instanzen in der Klasse Lift haben. Double hat zum Beispiel keine Instanz, aber man kann leicht eine erstellen:
instance Lift Double where
lift x = return $ LitE (RationalL (toRational x))
Der Lit-Datentyp hat keinen DoubleL-Konstruktor, aber RationalL kann an seiner Stelle verwendet werden, da er auf ein allgemeines Mitglied der Klasse Fractional spleißt.
Wenn Sie dies mit Funktionen verwenden wollen, die eine Mischung von Typen als Argumente annehmen, können Sie keine Liste übergeben, da Listen nicht von gemischten Typen sein können. Sie könnten dafür Tupel verwenden, was mit Template Haskell auch nicht viel schwieriger ist. In diesem Fall würden Sie eine Funktion erstellen, die den AST einer Funktion generiert, die ein Tupel mit den entsprechenden Typen darin nimmt und es auf den gewünschten Funktionsaufruf abbildet. Alternativ könnten Sie Ihre Argumenttypen in eine entsprechend gestaltete ADT verpacken, die Sie übrigens auch mit Template Haskell erstellen könnten. Dies sei dem Leser als Übung überlassen :)
Schließlich gelten alle Standardbeschränkungen von Template Haskell. Zum Beispiel können Sie diese Funktion aufgrund der GHC-Stage-Beschränkung nicht aus dem Modul aufrufen, in dem sie definiert ist.
Template Haskell ist lustig und interessant, aber um ganz ehrlich zu sein, ist die iso-rekursive Datentyplösung wahrscheinlich ein bisschen leistungsfähiger und erfordert offensichtlich nicht die zusätzliche Verwendung von TH. Ich werde zurückkommen und eine Fortsetzung posten, wenn ich das zum Laufen bringe :)