2009-08-18 8 views
15

Une personne sur Reddit a apporté ce code à mon attention:(émulé) Macros dans Haskell?

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

Qu'est-ce qui se passe ici est que nous avons un ensemble d'opérations que nous pouvons faire des choses avec, comme arrière ou obtenir sa queue ou la tête.

Impressionnant.

Ce que je veux faire, c'est entrer dans des éléments individuels et les changer pour de bon. Par exemple, je veux être en mesure de faire quelque chose comme ceci:

ns !! 0 

et obtenir quelque chose comme [print, 1], puis changer dernier élément, disons, 3,14 de sorte que la fonction imprimerait 3.14.

Est-ce possible chez Haskell ou devrais-je simplement revenir à LISP?

UNE ÉDITION IMPORTANTE: J'ai en quelque sorte gaffé. Je comprends que je vais devoir créer une nouvelle liste. Est-il possible d'obtenir les arguments d'une fonction, qui fait partie d'une liste? Ce que je veux, c'est la capacité à composer des fonctions à partir de leurs identifiants/arguments et être capable de décomposer une fonction en identifiant/argument avant qu'elle ne soit évaluée.

+0

btw: qu'est-ce que vous en avez vraiment besoin? – yairchu

Répondre

10

C'est un peu plus compliqué que dans Lisp, mais pour la métaprogrammation dans Haskell, vous pouvez utiliser Template Haskell.

Par ex, [|print 1|] sera traduit à

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

qui a le type Q Exp (une citation d'une expression).

Si vous souhaitez fusionner vos propres données dans un devis, [|print $(foo 3.14)|] exécutera foo 3.14 lors de la compilation.

4

Vous souhaitez faire muter la liste? Vous devriez retourner à lisp ;-)

Les valeurs sont immuables dans Haskell. Le Haskell-way est de créer une nouvelle liste qui est équivalente à l'ancienne liste à l'exception du dernier élément.

(Il y a quelques trucs impliquant monades où vous pouvez simuler des valeurs mutables et des pointeurs, mais qui est sans doute pas ce que vous voulez ici.)

EDIT: Pas tout à fait sûr que je comprends la question éditée, mais vous pouvez gérer la fonction et l'argument séparément comme des données, puis «appliquer» plus tard, par exemple.

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

Je crains de ne pas avoir formulé la question correctement. Je ne veux vraiment pas muter quelque chose. Je veux être capable de lire l'identifiant/les arguments de la fonction avant qu'ils ne soient évalués et aussi de composer des instructions à partir de fonctions/arguments génériques. Modifié. – mannicken

11

Une fois que vous avez appliqué une valeur à une fonction, il n'y a aucun moyen de la récupérer. Essayez d'encapsuler la fonction et son argument dans un type de données que vous pouvez évaluer ou décomposer en fonction de vos besoins.

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 

Sorties

1 
2 
3 
1 
4 
9 
+0

btw, vous pouvez utiliser des tuples comme (impression, 1) au lieu de App et "ID uncurry" au lieu de runApp – yairchu

+0

Ah, les joies de la notation sans points. Pourtant, j'aime avoir des fonctions avec des noms spécifiques à la tâche à accomplir –

1

Comme a été dit que la façon de haskell est de créer simplement une nouvelle liste, mais vous pouvez avoir des tableaux mutables à l'intérieur du monade IO avec IOArray si vous voulez vraiment

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