5

関数から始めたとしましょう

fromJust Nothing = error "fromJust got Nothing!"
fromJust (Just x) = x

次に、エラーメッセージを改善するために、テンプレートHaskellを介してソース情報を追加したいと思います。関数にパラメーターを追加できると想像してみましょう

fromJust' loc Nothing = error $ "fromJust got Nothing at " ++ (loc_filename loc)
fromJust' loc (Just x) = x

次に、次のfromJustようなソースコードで使用できるマクロがあります。

x = $fromJust $ Map.lookup k m

ハック

準引用符を使用し、ソースファイル名の文字列を持ち上げることで、なんとかハッキングできました。LocLiftインスタンスがないようです。もっと良い方法はありますか?

fromJustErr' l (Nothing) =
    error $ printf "[internal] fromJust error\
        \\n    (in file %s)" l
fromJustErr' l (Just x) = x
fromJustErr = do
    l <- location
    let fn = loc_filename l
        fnl :: Q Exp = TH.lift fn
    [| fromJustErr' $fnl |]

ありがとう!

(使用するよりもファンクターfmapを介して詰め込む方が良いことは知っていますが、時々ハックする必要があります。)MaybefromJust

4

2 に答える 2

4

このパターンをいくらか再利用可能にする試みがあります。

error重要なアイデアは、エラーメッセージに場所を含めるようにカスタマイズされた関数を渡すことです。次のように使用します。

fromJust' :: (String -> a) -> Maybe a -> a
fromJust' error Nothing = error "fromJust got Nothing!"
fromJust' error (Just x) = x

fromJust :: Q Exp
fromJust = withLocatedError [| fromJust' |]

この関数の使用は、元のアプローチと似ています。

main = print (1 + $fromJust Nothing)

さて、これを機能させるテンプレートHaskellの場合:

withLocatedError :: Q Exp -> Q Exp
withLocatedError f = do
    let error = locatedError =<< location
    appE f error

locatedError :: Loc -> Q Exp
locatedError loc = do
    let postfix = " at " ++ formatLoc loc
    [| \msg -> error (msg ++ $(litE $ stringL postfix)) |]

formatLoc :: Loc -> String
formatLoc loc = let file = loc_filename loc
                    (line, col) = loc_start loc
                in concat [file, ":", show line, ":", show col]

locatedError場所を指定して、カスタマイズさerrorれた関数を生成します。withLocatedErrorこれをにフィードしてfromJust'、すべてをフックします。formatLoc場所を文字列にうまくフォーマットするだけです。

これを実行すると、必要な結果が得られます。

FromJustTest: fromJust got Nothing! at FromJustTest.hs:5:19
于 2011-08-16T11:33:50.263 に答える
1

新しい誤差関数を作ってみませんか?

locError :: Q Exp
locError = do
    loc <- location
    msgName <- newName "msg"
    eError <- [|error|]
    eCat <- [|(++)|]
    let
        locStr = loc_filename loc
        locLit = LitE (StringL locStr)
        pat    = VarP msgName
        body   = AppE eError locLit
    return $ LamE [pat] body

次に、次のように使用します

foo :: Int
foo = $(locError) "This is an error"

(それは不完全です-メッセージを与えるのではなく、ファイルだけを与えますが、あなたは考えを理解します)

編集

あなたの質問を読み直すと、これはあなたがやろうとしていることではないことがわかります。これは興味深いアイデアです。発信者の位置情報を取得しようとしていますが、スタックトレースのようなものですが、深さは1層だけです。それがどのように機能するかわかりません。

locError同じトリックを使って作ることもできると思いますがlocFromJust、一般的な方法が必要でしたが、そうではありません。

于 2011-08-16T05:16:11.317 に答える