質問
私は次のようなコードをたくさん書いていることに気づきました
putStr "foo (bar 1) (bar 2) =" print $ foo (bar 1) (bar 2)
問題は、出力されたメッセージが実際に実行されたコードと同期しなくなる可能性があることです。明らかな解決策は、このコードを自動生成することです。
そのための 1 つの方法は、すべてのテキストをファイルに配置し、そのファイルを読み取り、そこから Haskell ソース コードを生成する小さなプログラムを作成することです。しかし、もう 1 つの選択肢は、Template Haskell を使用することです。
を受け取る関数をどのように書くか知っている人はいますか? String
そこから上記のコードを生成しますか?かなり簡単だと思いますが、TH については十分に文書化されていません。
解決
haskell-src-meta
パッケージを使用してHaskellコードを解析できます。これはあなたがテンプレート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)|]
. 他のヒント
テンプレート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))
. という例があります eval
GHC APIを使用したHaskellのようなコード ここ.
痛い。 i 思想は簡単ですが、私が知ることができる限り、それは実際には不可能な。
私は文字列を式に変える関数があることを期待していましたが、明らかにそのような関数は存在しませんでした。ディスクからより多くのソースコードをロードする機能さえありません。だからこの仕事は実際に不可能であるようです!私はそれにかなり驚いています。
私ができる最も近いものは、実行したい式を引用し、それを実行する前に引用符で囲まれた式を印刷するスプライスを作成することです。しかし、それは私をGHCの表現のきれいプリンタの憐れみを置きます。ラベルは、入力したときにがに出ません。 (特に、演算子に置き換えるようです、これは単に痛みを伴う)
あなたはこのような機能を実装するのにかなり些細なことだと思ったでしょう。したがって、実装されていないという事実は、2つのことのうちの1つにのみ帰属することができます:
-
だれもこの機能を実際に必要としません。 (私を除く、明らかに。)
-
見た目のように些細ではありません。 (例えば、表現を解析するためにどのような文脈をいっぱいに解析するかどうかはどういうわけかどうか?)
を使用することもできます。 ごみ この正確なユースケースを処理するために作成されたパッケージ:
{-# 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 パッケージマネージャー)この「ワンライナー」を使ってすぐに試してみることもできます。
$ 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"