(模拟)Haskell中的宏?

man*_*ken 16 lisp macros haskell 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
Run Code Online (Sandbox Code Playgroud)

这里发生的是我们有一系列操作,我们可以做的事情,如反向或得到它的尾巴或头部.

真棒.

我想要做的是进入单个元素并改变它们.例如,我希望能够做到这样的事情:

ns !! 0
Run Code Online (Sandbox Code Playgroud)

并获得类似[print,1]的内容,然后将最后一个元素更改为3.14,以便该函数将打印3.14.

在Haskell中是否可能,或者我应该回到LISP?

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

Jos*_*Lee 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
Run Code Online (Sandbox Code Playgroud)

输出

1
2
3
1
4
9
Run Code Online (Sandbox Code Playgroud)


Ale*_*nov 11

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

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

return $ AppE (VarE $ mkName "print") (LitE $ IntegerL 1)
Run Code Online (Sandbox Code Playgroud)

具有类型Q Exp(表达式的引用).

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