2012-03-06 59 views
4

考慮以下代碼:爲什麼這個模板Haskell的工作?

 
magic :: String -> Q Exp 
magic s = [e| putStrLn s |] 

現在,作爲最好的,我可以告訴大家,這不應該實際工作。在牛津括號內,s不在範圍內。然而,上述顯然完美的作品。

如果我們稍微改變這個例子,現在打破可怕:

 
magic :: Exp -> Q Exp 
magic (VarE n) = [e| putStrLn (nameBase n) |] 

和以前一樣,我們在範圍上有一個變量不是。和這個時間,它打破。但它並不抱怨一個不在範圍內的變量;相反,它會抱怨一些缺乏實例的無證班級。

任何人都知道這是怎麼回事?

+0

你如何運行Q monads?通常情況下,編譯時會完成,但有些使用會在運行時結束,而且事情更加脆弱。 – 2012-03-06 17:18:39

回答

12

s牛津括號內的範圍。基本上,您可以在引用的表達式中使用多種類型的值(包含Lift實例的值),並且它們會自動轉換爲適當的代碼以在另一端重新創建相應的值。

例如,Lift instance for Integers只是構造相應的整數文字,而instance for Maybe只是構造適當的構造函數應用程序。你甚至可以定義你自己的Lift的實例。

由於nName,因此不是Lift才能得到「無實例」錯誤。

2

我想簡短的回答是,魔術功能預計將工作,因爲引號括號捕捉其局部變量(以某種方式)。本質上,引號括起來的編譯時局部變量被它們的字面值取代併成爲運行時常量。這是通過隱式調用lift函數來實現的,所以[| .. var .. |]變成[| $(lift var)|]。

也許你會將這種行爲與它們唯一捕獲局部變量的事實相混淆,這樣重複調用相同的引號將不會干擾彼此的變量名稱。這是通過在幕後調用newName來實現的,這確保了唯一的變量名稱。

如果有幫助,我個人認爲引號括號是「拼接生成器」 - 在編譯時將被轉換爲AST的一小段Haskell代碼,因此可以將拼接準備插入任何地方。正如Bulat的教程(links from)指出的那樣,它們就像一種宏預處理器,因爲它們是Haskell函數生成代碼的混合體,並且簡單地將Haskell代碼自動轉換爲TH AST。

編輯:似乎ehird打敗我的答案 - 我離開我的答案,以防萬一它提供了一些額外的價值。