(эмулированные) макросы в Haskell?
Человек из Reddit довел этот код до моего внимания:
main = do
let ns = [print 1, print 2, print 3]
sequence_ ns
sequence_ $ reverse ns
sequence_ $ tail ns ++ [head ns]
head ns
Что происходит здесь, мы имеем целый ряд операций, которые мы можем делать, например, наоборот, или получить хвост или голову.
Высокий.
То, что я хочу сделать, - это разобраться в отдельных элементах и изменить их навсегда. Например, я хочу иметь возможность сделать что-то вроде этого:
ns !! 0
и получить что-то вроде [print, 1], а затем изменить последний элемент, скажем, на 3.14, чтобы функция печатала 3.14.
Возможно ли это в Haskell, или я просто вернусь к LISP?
ВАЖНЫЙ РЕДАКТ: Я как бы ошибался. Я понимаю, что мне нужно будет создать новый список. Можно ли получить аргументы функции, которая является частью списка? То, что я хочу, - это возможность составлять функции из своих идентификаторов/аргументов, а также возможность разбивать функцию на идентификатор/аргумент, прежде чем он будет оценен.
Ответы
Ответ 1
Это немного сложнее, чем в Lisp, но для метапрограммирования в Haskell вы можете использовать Template Haskell.
Например, [|print 1|]
будет переведен на
return $ AppE (VarE $ mkName "print") (LitE $ IntegerL 1)
который имеет тип Q Exp
(цитата выражения).
Если вы хотите объединить свои данные в цитату, [|print $(foo 3.14)|]
выполнит foo 3.14
во время компиляции.
Ответ 2
Как только вы применили значение к функции, нет способа вернуть ее. Попробуйте обернуть функцию и ее аргумент в тип данных, который вы можете оценить или разложить в зависимости от ваших потребностей.
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
Ответ 3
Вы хотите изменить список? Вы должны вернуться к lisp; -)
Значения неизменны в Haskell. Haskell-way должен создать новый список, который эквивалентен старому списку, за исключением последнего элемента.
(Есть несколько трюков с участием монад, где вы можете имитировать изменяемые значения и указатели, но это, вероятно, не то, что вы хотите здесь.)
EDIT: не совсем уверен, что я понимаю отредактированный вопрос, но вы можете обрабатывать функцию и аргумент отдельно как данные, а затем "применять" позже, например.;
do
let ns = [(print, 1), (print, 2), (print, 3)]
sequence_ $ map (\(f,a)->f a) ns
Ответ 4
Как было сказано, способ 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