2012-12-29 19 views
4

我是Haskell的新手。鉴于Haskell的整个前提是一个函数总是会返回相同的值,所以我希望有一些例如在编译时计算常量的斐波那契数值,就像我可以在C++中使用模板metaprogrmming所做的那样,但我看不出如何去做。有没有办法?Haskell变体模板元编程

+3

[模板哈斯克尔](http://www.haskell.org/haskellwiki/Template_Haskell),也许?它甚至比C++中的模板元编程稍微少一些,尽管这并不多。两者都不是很有趣。 :P –

+0

@ C.A.McCann它可以用于fibs吗?我的理解是TH更像C宏,但这可能是完全错误的。 (也就是说,模板C++是一种刚刚荣耀的宏,嗯,我猜可能在编译时只通过宏来定义C中的fibs,不知道......)无论如何,具体的例子会有所帮助。 – lobsterism

+4

TH是在编译时执行的任意Haskell代码,可以生成语法树来将定义,表达式或其他代码拼接到代码中。 C宏甚至没有开始比较。它远不如C++模板垃圾那样有限,使用任何非平凡的东西都是一种痛苦,因为使用AST数据类型很笨拙。 –

回答

9

编辑:丹尼尔菲舍尔指出可以解除一个普通的表达成模板Haskell和评估在编译时的结果,但须在输出类型的某些约束,由具有通常的功能fib然后接合

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

原始答案如下。

正如在评论中指出的,模板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 
+8

在另一个模块中有一个普通的'fib'函数,然后拼接'y = $(let x = fib 1000 in [| x |])''是不是更好? –

+0

@DanielFischer我不认为这样做,这是更好。 –

5

Don Stewart有一个great article他在那里表明,使用具有正确标志选择的LLVM后端将在编译时预先计算某些函数并将其替换为常量。

+0

嗯,有点类似,但它似乎相当试验和错误(虽然有组织地如此)而不是直接确定性。 C++模板更多的是后者;所见即所得,我希望能有更多这样的解决方案。 – lobsterism

+0

@lobsterism然后你想要模板哈斯克尔。我只提到这个解决方案,因为你在问我们为什么不能利用函数的纯度来预编译它们。然而,Haskell模板根本不利用纯度,甚至有副作用。 –