7

私は自分が次のような多くのコードを書いていることに気づきます

putStr "foo (バー 1) (バー 2) ="
print $ foo (バー 1) (バー 2)

問題は、出力されたメッセージが実際に実行されたコードと同期しなくなる可能性があることです。明らかな解決策は、このコードを自動生成することです。

これを行う 1 つの方法は、すべてのテキストをファイルに入れ、そのファイルを読み取り、そこから Haskell ソース コードを生成する小さなプログラムを作成することです。しかし、もう 1 つの方法は Template Haskell を使用することです。

Stringを受け取り、そこから上記のコードを生成する関数を作成する方法を知っている人はいますか? かなり簡単だと思いますが、TH は十分に文書化されていません。

4

5 に答える 5

10

haskell-src-metaパッケージを使用して Haskell コードを解析できます。これを Template Haskell と組み合わせる方法の簡単な例を次に示します。

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH
import Language.Haskell.TH.Quote
import Language.Haskell.Meta

runShow = QuasiQuoter
    { quoteExp  = runShowQQ
    , quotePat  = undefined
    , quoteType = undefined
    , quoteDec  = undefined
    }

runShowQQ :: String -> Q Exp
runShowQQ s = do
    let s'          = s ++ " = "
        Right exp = parseExp s
        printExp  = appE [|print|] (return exp)
    infixApp [|putStr s'|] [|(>>)|] printExp

そして、あなたはそれをこのように使うでしょう

{-# LANGUAGE QuasiQuotes #-}

[runShow|foo (bar 1) (bar 2)|]
于 2012-02-29T13:13:07.213 に答える
4

テンプレート Haskell は、任意の文字列を解析する簡単な手段を提供していないため、最も簡単な解決策はおそらく C プリプロセッサを使用することです。ただし、GHC に組み込まれているものは文字列化をサポートしていないため、代わりに「本物の」ものを使用するには追加のオプションを渡す必要があります。

{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -pgmP cpp #-}

#define PRINT_EXP(x) (putStr #x >> putStr " = " >> print (x))

その後、次のように使用できます。

PRINT_EXP(foo (bar 1) (bar 2))
于 2012-02-29T13:09:53.700 に答える
1

evalGHC API を使用した Haskell のようなコードの例がここにあります

于 2012-02-29T12:14:14.537 に答える
0

痛い。これは簡単だと思いましたが、私が知る限り、それは実際には不可能です。

文字列を式に変換する関数があると思っていたのですが、どうやらそのような関数は存在しないようです。ディスクからより多くのソースコードをロードする機能すらありません。したがって、このタスクは実際には不可能のようです。びっくりしました。

私ができる最も近いことは、実行したい式を引用し、実行する前に引用された式をきれいに印刷するスプライスを作成することです。しかし、それは私をGHCの表現のかわいいプリンターに翻弄されます。入力したとおりにラベルが出てこない。(特に、演算子を完全修飾名に置き換えるようですが、これは非常に面倒です。)

このような機能を実装するのは非常に簡単だと思っていたでしょう。したがって、実装されていないという事実は、次の2つのいずれかにのみ起因する可能性があります。

  1. 誰も実際にこの機能を必要としません。(まあ、私を除いて、明らかに。)

  2. 見た目ほど簡単ではありません。(たとえば、式を解析するコンテキストを理解するのはどういうわけか面倒ですか?)

于 2012-02-29T13:19:46.950 に答える
0

この正確なユースケースを処理するために書かれたダンプパッケージを使用することもできます:

{-# language QuasiQuotes #-}
import Debug.Dump

main = putStrLn [d| foo (bar 1) (bar 2) |]

foo = (+)
bar = (+1)

どちらが印刷されますか:(foo (bar 1) (bar 2)) = 5

コンマで区切られた複数の式も処理します。

putStrLn [d| foo (bar 1) (bar 2), map bar [1, 2] |]

どちらが印刷されますか:(foo (bar 1) (bar 2)) = 5 (map bar [1, 2]) = [2,3]

おまけ: nix-shell ( nix package managerの一部) がインストールされている場合は、この「ワンライナー」ですぐに試すこともできます:

$ nix-shell -p "nix-shell -p "haskellPackages.ghcWithPackages (p: [p.dump])" --run "echo '{-# language QuasiQuotes #-}; import Debug.Dump; foo = (+); bar = (+1); main = putStrLn [d| foo (bar 1) (bar 2), map bar [1, 2] |]' | runhaskell"

于 2016-09-25T15:52:32.063 に答える