【问题标题】:Haskell variant of template metaprogramming模板元编程的 Haskell 变体
【发布时间】:2012-12-29 02:20:00
【问题描述】:

我是 Haskell 的新手。鉴于 Haskell 的整个前提是函数将始终返回相同的值,我希望有某种方式,例如在编译时计算常量的斐波那契值,就像我可以在 C++ 中使用模板元编程一样,但我不知道该怎么做。有什么办法吗?

【问题讨论】:

  • Template Haskell,也许?它甚至比 C++ 中的模板元编程更糟糕,尽管这并不能说明什么。两者都不是很有趣。 :P
  • @C.A.McCann 它是否适用于 fib?我的理解是 TH 更像是 C 宏,但这可能是完全错误的。 (也就是说,模板 C++ 只是一种美化的宏,嗯,我想有可能在编译时通过宏在 C 中定义 fibs,不确定......)无论如何,具体的例子会有所帮助。
  • TH 是任意的 Haskell 代码,在编译时执行,它可以生成语法树以将定义、表达式或任何其他内容拼接到代码中。 C 宏甚至还没有开始比较。它远没有 C++ 模板垃圾那么有限,因为使用 AST 数据类型很笨拙,所以使用它只是一种痛苦。
  • 我发现如果 Lisp 有实际语法,它与 Lisp 宏的感觉非常相似

标签: haskell template-haskell


【解决方案1】:

编辑:Daniel Fischer 指出,您可以将普通表达式提升到 Template Haskell 并在编译时评估结果,但要受到输出类型的某些限制,方法是使用普通函数 fib 然后拼接

$(let x = fib 1000 in [|x|])

原始答案如下。

正如 cmets 中所指出的,Haskell 模板是实现此目的的方法。对于像斐波那契这样的归纳函数,它相当简单。您编写类似于标准定义的代码,但返回一个 ExpQ 值。由于拼接限制,您需要使用 2 个模块。

{-# LANGUAGE TemplateHaskell #-} 
module TH where

import Language.Haskell.TH

fibTH :: Int -> ExpQ
fibTH 0 = [| 0 |]
fibTH 1 = [| 1 |]
fibTH n = [| $(fibTH (n-1)) + $(fibTH (n-2)) |]

{-# LANGUAGE TemplateHaskell #-}
module Main where

import TH

y :: Int
y = $(fibTH 10)

main = print y

要确认工作是在编译时执行的,我们可以用-ddump-simpl 编译来查看内核,这可以确认。

Main.y :: GHC.Types.Int
[GblId,
 Caf=NoCafRefs,
 Str=DmdType m,
 Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=True,
         ConLike=True, WorkFree=False, Expandable=True,
         Guidance=IF_ARGS [] 10 20}]
Main.y = GHC.Types.I# 55

【讨论】:

  • 在另一个模块里有一个普通的像样的fib函数然后拼接y = $(let x = fib 1000 in [|x|])不是更好吗?
  • @DanielFischer 我没想过要尝试,这样会更好。
【解决方案2】:

Don Stewart 的 great article 表明,使用 LLVM 后端并选择正确的标志将在编译时预先计算某些函数并用常量替换它们。

【讨论】:

  • 嗯,有点,但它似乎是反复试验(尽管是有组织的),而不是直接确定。 C++ 模板更多是后者。所见即所得,我希望有更多这样的解决方案。
  • @lobsterism 然后你需要 Template Haskell。我只提到了这个解决方案,因为你在问为什么我们不能利用函数的纯度来预编译它们。然而,Haskell 模板根本没有利用纯度,它甚至会产生副作用。
猜你喜欢
  • 2013-05-06
  • 2014-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-25
相关资源
最近更新 更多