2009-08-18 1 views
15

Люди на Reddit принес этот код моего внимания:(эмулируется) Макросы в Haskell?

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

Что происходит здесь мы имеем массив операций, которые мы можем сделать материал с, как реверс или получить его хвост или голова.

Awesome.

Что я хочу сделать, это разобраться в отдельных элементах и ​​изменить их навсегда. Например, я хочу, чтобы иметь возможность сделать что-то вроде этого:

ns !! 0 

и получить что-то вроде [печать, 1], а затем изменить последний элемент, скажем, 3,14, так что функция будет печатать 3.14.

Возможно ли это в Haskell или я должен просто вернуться к LISP?

ВАЖНЫЙ РЕДАКТОР: Я как бы ошибался. Я понимаю, что мне нужно будет создать новый список. Можно ли получить аргументы функции, которая является частью списка? То, что я хочу, - это возможность составлять функции из своих идентификаторов/аргументов, а также иметь возможность разбивать функцию на идентификатор/аргумент перед его оценкой.

+0

кстати: для чего вам это действительно нужно? – yairchu

ответ

10

Это немного сложнее, чем в Lisp, но для метапрограммирования в Haskell вы можете использовать Template Haskell.

например, [|print 1|] будут переведены на

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

, который имеет тип Q Exp (цитата из выражения).

Если вы хотите объединить свои данные в котировку, [|print $(foo 3.14)|] выполнит foo 3.14 во время компиляции.

4

Вы хотите изменить список? Вы должны вернуться к lisp ;-)

Ценности неизменны в Haskell. Haskell-way должен создать новый список, который эквивалентен старому списку, за исключением последнего элемента.

(Есть некоторые приемы, связанные с монады, где можно симулировать изменяемые значения и указатели, но это, вероятно, не то, что вы хотите здесь.)

EDIT: Не совсем уверен, что я понимаю, отредактированный вопрос, но вы можете обрабатывать функцию и аргумент отдельно как данные, а затем «применять» позже, например;

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

Боюсь, я не сформулировал вопрос правильно. Я действительно не хочу ничего мутировать. Я хочу иметь возможность читать функциональный идентификатор/аргументы, прежде чем они будут оцениваться, а также составить инструкции из общих функций/аргументов. Отредактировано. – mannicken

11

Как только вы применили значение к функции, нет способа вернуть его. Попробуйте включить функцию и ее аргумент в тип данных, который вы можете оценить или разложить в зависимости от ваших потребностей.

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 

Выходы

1 
2 
3 
1 
4 
9 
+0

Кстати, вы можете использовать кортежи вроде (print, 1) вместо App и «uncurry id» вместо runApp – yairchu

+0

Ах, радости бесплатных ссылок. Тем не менее, мне нравятся функции с именами, специфичными для задачи –

1

Как было сказал Haskell способ просто создать новый список, но вы можете иметь изменяемые массивы внутри монады IO с IOArray, если вы действительно хотите

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