2009-08-18 8 views
15

Una persona su Reddit ha portato questo codice alla mia attenzione:(emulato) Macro in Haskell?

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

Che cosa sta succedendo qui è che abbiamo una serie di operazioni che possiamo fare cose con, come inversa o ottenere la coda o la testa.

Impressionante.

Quello che voglio fare è entrare nei singoli elementi e cambiarli per sempre. Per esempio, io voglio essere in grado di fare qualcosa del genere:

ns !! 0 

e ottenere qualcosa di simile [stampa, 1] e poi cambiare ultimo elemento, per esempio, 3.14 in modo che la funzione sarebbe stampare 3.14.

È possibile a Haskell o dovrei semplicemente tornare a LISP?

UN MODIFICO IMPORTANTE: ho una specie di errore. Capisco che avrò bisogno di creare una nuova lista. È possibile ottenere gli argomenti di una funzione, che è una parte di una lista? Quello che voglio è la capacità di comporre funzioni dai loro identificatori/argomenti e anche essere in grado di suddividere una funzione in identificatore/argomento prima che venga valutata.

+0

btw: a cosa serve davvero? – yairchu

risposta

10

È un po 'più complicato rispetto a Lisp, ma per metaprogrammazione in Haskell, è possibile utilizzare Template Haskell.

esempio [|print 1|] sarà tradotto per

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

che ha il tipo Q Exp (citazione di un'espressione).

Se si desidera unire i propri dati in un preventivo, [|print $(foo 3.14)|] eseguirà foo 3.14 in fase di compilazione.

4

Si desidera modificare l'elenco? Si dovrebbe tornare a Lisp ;-)

I valori sono immutabili in Haskell. Il modo Haskell è quello di creare un nuovo elenco che è equivalente alla vecchia lista fatta eccezione per l'ultimo elemento.

(Ci sono alcuni trucchi che coinvolgono monadi in cui è possibile simulare i valori mutevoli e puntatori, ma che probabilmente non è ciò che si vuole qui.)

EDIT: Non del tutto sicuro di aver capito la domanda modificato, ma è possibile gestire le funzioni e le argomentazioni separatamente come dati e quindi "applicare" successivamente, ad esempio;

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

Ho paura di non aver formulato correttamente la domanda. Non voglio davvero mutare nulla. Voglio essere in grado di leggere l'identificatore di funzione/argomenti prima che vengano valutati e anche comporre dichiarazioni da funzioni/argomenti generici. Modificato. – mannicken

11

Una volta applicato un valore a una funzione, non è possibile recuperarlo. Prova a inserire la funzione e il suo argomento in un tipo di dati che puoi valutare o scomporre in base alle tue esigenze.

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 

uscite

1 
2 
3 
1 
4 
9 
+0

btw, puoi usare tuple come (print, 1) invece di App e "uncurry id" invece di runApp – yairchu

+0

Ah, le gioie della notazione point-free. Tuttavia, mi piace avere funzioni con nomi specifici per l'attività in corso –

1

Come è stato detto che il modo Haskell è quello di creare semplicemente una nuova lista, ma si può avere array mutevoli all'interno della monade IO con IOArray se si vuole veramente

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