2012-12-29 17 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模板根本不利用純度,甚至有副作用。 –