2009-08-18 13 views
15

Eine Person auf Reddit hat diesen Code, um meine Aufmerksamkeit gebracht:(emuliert) Makros in Haskell?

main = do 
    let ns = [print 1, print 2, print 3] 
    sequence_ ns 
    sequence_ $ reverse ns 
    sequence_ $ tail ns ++ [head ns] 
    head ns 

Was hier los ist, dass wir eine Reihe von Operationen, die wir Sachen mit, wie umgekehrt tun oder mit dem Schwanz oder Kopf.

Ehrfürchtig.

Was ich tun möchte, ist in einzelne Elemente zu kommen und sie für immer zu ändern. Zum Beispiel möchte ich so etwas wie dies in der Lage sein zu tun:

ns !! 0 

und bekommen so etwas wie [print, 1] und dann letzte Element ändern, sagen wir, 3,14, so dass die Funktion würde 3,14 drucken.

Ist es in Haskell überhaupt möglich oder sollte ich einfach zurück zu LISP gehen?

EIN WICHTIGER BEARBEITUNG: Ich habe etwas getäuscht. Ich verstehe, dass ich eine neue Liste erstellen muss. Ist es möglich, die Argumente einer Funktion zu bekommen, die Teil einer Liste ist? Was ich will, ist die Fähigkeit, Funktionen aus ihren Bezeichnern/Argumenten zusammenzusetzen und auch eine Funktion in Bezeichner/Argument zu zerlegen, bevor es ausgewertet wird.

+0

BTW: Wofür brauchst du das eigentlich? – yairchu

Antwort

10

Es ist ein bisschen komplizierter als in Lisp, aber für Metaprogrammierung in Haskell können Sie Template Haskell verwenden.

Z.B. [|print 1|] wird

return $ AppE (VarE $ mkName "print") (LitE $ IntegerL 1) 

die den Typ hat Q Exp übersetzt werden (ein Zitat eines Ausdrucks).

Wenn Sie Ihre eigenen Daten in ein Angebot einfügen möchten, führt [|print $(foo 3.14)|]foo 3.14 zur Kompilierzeit aus.

4

Sie möchten die Liste mutieren? Sie sollten zurück zu Lispeln gehen ;-)

Werte sind unveränderlich in Haskell. Der Haskell-Weg besteht darin, eine neue Liste zu erstellen, die der alten Liste bis auf das letzte Element entspricht.

(Es gibt einige Tricks Monaden bestehen, bei denen Sie wandelbar Werte und Zeiger simulieren können, aber das ist wahrscheinlich nicht das, was Sie hier wollen.)

EDIT: Nicht ganz sicher, ob ich die bearbeitete Frage verstehen, aber man kann Handle Funktion und Argument getrennt als Daten und dann "anwenden" später, z.

do 
    let ns = [(print, 1), (print, 2), (print, 3)] 
    sequence_ $ map (\(f,a)->f a) ns 
+1

Ich fürchte, ich habe die Frage nicht richtig formuliert. Ich möchte wirklich nichts mutieren. Ich möchte in der Lage sein, Funktionsbezeichner/Argumente zu lesen, bevor sie ausgewertet werden, und auch Anweisungen aus generischen Funktionen/Argumenten zu verfassen. Bearbeitet. – mannicken

11

Sobald Sie einen Wert auf eine Funktion angewendet haben, gibt es keine Möglichkeit, es zurück zu bekommen. Versuchen Sie, die Funktion und ihr Argument in einem Datentyp zu verpacken, den Sie je nach Bedarf auswerten oder zerlegen können.

data App a b = App (a -> b) a 
runApp (App a b) = a b 
ns = [App print 1, App print 2, App print 3] 
main = do 
    sequence_ $ map runApp ns 
    let ns2 = [App fun (arg^2) | App fun arg <- ns] 
    sequence_ $ map runApp ns2 

Ausgänge

1 
2 
3 
1 
4 
9 
+0

BTW, können Sie Tupel wie (Print, 1) anstelle von App und "uncurry ID" anstelle von runApp – yairchu

+0

Ah, die Freuden der Punkt-freie Notation verwenden. Trotzdem mag ich Funktionen mit Namen, die für die jeweilige Aufgabe spezifisch sind –

1

Wie gesagt wurde, die Haskell Weg ist, einfach eine neue Liste zu erstellen, aber Sie können änderbare Arrays innerhalb des IO Monade mit IOArray haben, wenn Sie wirklich

import Data.Array.IO 

seqArr_ arr = getElems arr>>=sequence_ 

main= do 
    arr <- newListArray (0,2) [print 1,print 2,print 3] :: IO (IOArray Int (IO())) 
    seqArr_ arr -- prints 1 2 3 
    writeArray arr 2 (print 3.14) -- change the last element 
    seqArr_ arr -- prints 1 2 3.14 
wollen