2015-08-24 46 views
3

我在寫第一個ppx擴展。這個想法是支持多態的print函數,類似於Haskell中的show基本類型驅動ppx重寫

(我知道,其他更強大的解決方案存在,但我想更多地瞭解如何工作的。)

我採取的方法是非常相似的方法來描述here:我有一個mapper查找%[print <expr>]標記,然後用<expr>的字符串表示替換它們。例如,

[%print 1] ==> string_of_int 1 
[%print "aksljd"] ==> "aksljd" 

這正常工作對常量表達式,但我想,以支持地方<expr>任意表達式。它應該用最終類型的打印機包裝它們。

我目前的做法是使用Typecore.type_expression把一個Parsetree.expressionTypedtree.expression,再搭配上Typedtree.expressionexp_type領域,並確定如何處理替換整個表達式。例如,對於type test = A of int | B of string類型,我會將[%print A 1]替換爲show_test (A 1)那裏(show_test必須按照約定存在)。

這不起作用,因爲Typecore.type_expression將類型環境作爲參數,並且在重寫時無法獲取'當前類型環境',因爲還沒有執行類型檢查。 [%print 1 + 1]Typecore.type_expression Env.empty導致Unbound value +,因爲它應該。

有沒有人有解決這個問題?如果我完全走錯了方向,請隨時指出。 :p

+0

這是什麼意思:'let f x = [%print x]'? – antron

+0

好點。是的,多態性不適用於此。我沒有完全想到所有東西,這只是一個天真的第一次嘗試學習ppx :) – user1953221

+0

給出這些信息,如何才能調用'show'的等效函數print :: Show a => a - > String'在Haskell的參數工作?在通過類型檢查之後,編譯器是否將它內聯,然後用其靜態已知的實現替換show的每個實例? – user1953221

回答

3

評論中的討論摘要:show在Haskell中不能像這樣工作(也不能)。 Haskell中的Show a =>將被轉換爲OCaml中的顯式模塊參數。該模塊將具有像print : a -> string這樣的簽名值。 Haskell通過找到您(或庫)已聲明的aShow實例來推斷此模塊參數,但在OCaml中,您必須手動傳遞該模塊。在Haskell中聲明Show實例類似於在OCaml中實例化函子。

在這兩個Haskell和OCaml中,預處理表達式中的問題將有可疑的意義的情況下,如:

let f x = [%print x] 

如果f : 'a -> string(即沒有Haskell的類型類實例可用,沒有ocaml的模塊被傳遞)。

爲了使它更完整,許多其他分析類型的ppx重寫器從類型定義或聲明中生成代碼,而不是從表達式中生成代碼。

2

你想要做的事情基本上不支持ppx。正如你所說的,ppx是在解析之後,類型檢查還沒有完成。 由於編譯器的當前體系結構,鍵入檢查任意表達式非常困難。

如果你想破解你的方式,你可以閱讀this。 :) 作者將在2周的ocaml研討會上做一個關於它的演示文稿,應該有一個視頻。

+0

我不認爲它只是不被ppx支持。我認爲它在OCaml中並沒有明智的語義,除非提問者在'%print'可以使用時給出一些限制。而且它也不是它在Haskell中的工作方式。 – antron

+0

我看到......在這種情況下,編譯時元編程的限制取決於類型?我已經看到了一些很棒的解決方案,比如ppx_implicits和ppx_deriving,這些解決方案似乎取決於類型,但是我還沒有注意到可以靜態知道什麼和我在這裏要做什麼之間的區別。 – user1953221

+0

請注意那些繼續類型定義(有時聲明),而不是表達式。特別是在一個具體的單形類型定義中,您可以使用該類型的結構,並且沒有任何可推斷的內容,也沒有需要擔心的多態性。在抽象或多態類型的情況下,生成的值會將某些結構或處理委託給其他值。最後的陳述是一種手搖,因爲我已經沒有評論空間了。也許你應該問一個新問題? :) – antron

2

人們已經在一定程度上回答了......我希望我的回答可能會補充一些東西。

PPX是無類型 AST的預處理。它應該用於AST轉換,而不需要知道輸入的類型。

ppx_deriving和其他一些ppx預處理器爲每個類型定義和構造函數自動生成代碼。它們確實很酷,但它們完全在無類型層中工作:它們只是使用類型定義的語法結構。對於依賴於類型的行爲,您需要在無類型的AST中明確指定類型信息:[%derive.show: (int * int) list] [(1,2); (3,4); (5,6)][%derive.show] [(1,2); (3,4); (5,6)]不起作用。

因此,PPX的I/O是無類型的,但不限於在其內部無類型化。你可以做任何你喜歡的打字。一個典型的例子是將無類型輸入饋送到OCaml打字檢查器並獲得鍵入的 AST,然後使用獲得的打字信息達到您的目的。這種類型的PPX所需的工具已經在我們的手中:ppx_tools用於一般PPX工具,compiler-libs用於通過OCaml類型檢查器和預處理進行打字,並且將結果返回到無類型AST。該鏈接(https://bitbucket.org/camlspotter/compiler-libs-hack)@Drup指出如何使用它們。它是在PPX出來之前編寫的,但是在PPX中你所做的是非常相似的,而且我還爲該文章添加了一小部分用於PPX。

有類型的PPX只有一個缺點是它不能與REPL一起使用。 PPX逐個處理編譯單元,並且在每個單元處理之間傳送信息是有限的。在REPL中,編譯單元是一個頂級短語,爲了正確工作,有類型的PPX必須使頂級短語保持類型環境,這太大了。

+0

感謝您的洞察力。我已經多次閱讀過你的編譯器黑客文章,並仔細閱讀了源代碼中的暗示:) – user1953221