what's the correct way to have template haskell wrap a function with source information (e.g. line number)

不想你离开。 提交于 2019-12-05 11:17:27

Here's an attempt at making this pattern somewhat more reusable.

The key idea is to pass a customized error to our function which will include the location in the error message. You'd use it like this:

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

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

Using this function is similar to your original approach:

main = print (1 + $fromJust Nothing)

Now, for the Template Haskell that makes this work:

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 produces the customized error function, given a location. withLocatedError feeds this to fromJust' to hook everything together. formatLoc just formats the location nicely into a string.

Running this gives us the result we wanted:

FromJustTest: fromJust got Nothing! at FromJustTest.hs:5:19

How about making a new error function?

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

Then use it like

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

(It's incomplete -- doesn't give the message, just the file, but you get the idea)

EDIT

On re-reading your question, I realize this isn't what you're trying to do. It's an interesting idea -- you're trying to get caller location information -- sort of like a stack trace but only one layer deep. I have no idea how that would work.

Though I guess you could use the same trick of locError to make locFromJust -- but you wanted a general way, which this isn't.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!