(模拟)Haskell中的宏?

时间:2009-08-18 18:47:21

标签: haskell macros lisp lazy-evaluation

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?

重要编辑:我有点失误。我知道我需要创建一个新列表。是否有可能获得函数的参数,这是列表的一部分?我想要的是能够从标识符/参数中组合函数,并且能够在评估之前将函数分解为标识符/参数。

4 个答案:

答案 0 :(得分: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

答案 1 :(得分:11)

它比Lisp复杂一点,但对于Haskell中的元编程,你可以使用Template Haskell

例如,[|print 1|]将被翻译为

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

,其类型为Q Exp(表达式的引用)。

如果您想将自己的数据拼接成引号,[|print $(foo 3.14)|]将在编译时执行foo 3.14

答案 2 :(得分:4)

你想改变名单吗?你应该回到lisp; - )

值在Haskell中是不可变的。 Haskell方式是创建一个新列表,除了最后一个元素之外,它等同于旧列表。

(有一些涉及monad的技巧,你可以模拟可变的值和指针,但这可能不是你想要的。)

编辑:我不完全确定我理解编辑过的问题,但您可以将函数和参数分别作为数据处理,然后再“应用”,例如。

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

答案 3 :(得分:1)

就像已经说过的那样,haskell的方法就是创建一个新的列表,但如果你真的想要的话,你可以在IO monad中使用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