Как правильно добавить в шаблон haskell функцию информацию об источнике (например, номер строки) - PullRequest
5 голосов
/ 16 августа 2011

Предположим, я начинаю с функции

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

Затем я хочу добавить информацию об источнике через Template Haskell для улучшения сообщений об ошибках. Давайте представим, что я мог бы добавить дополнительный параметр к функции

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

и затем есть макрос fromJust, который я мог бы использовать в исходном коде, например,

x = $fromJust $ Map.lookup k m

1011 * хак * Мне удалось взломать его, используя квазицитаты и подняв строку исходного файла. Кажется, что у Loc нет экземпляра Lift. Есть ли лучший способ? 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 вещи через функтор Maybe, чем использовать fromJust, но мне иногда нужно взламывать.)

Ответы [ 2 ]

4 голосов
/ 16 августа 2011

Вот попытка сделать этот шаблон несколько более пригодным для повторного использования.

Основная идея состоит в том, чтобы передать в нашу функцию настроенный 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
1 голос
/ 16 августа 2011

Как насчет создания новой функции ошибки?

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"

(Это неполно - не дает сообщение, только файл, но вы поняли)

EDIT

Перечитывая ваш вопрос, я понимаю, что это не то, что вы пытаетесь сделать. Это интересная идея - вы пытаетесь получить информацию о местоположении caller - что-то вроде трассировки стека, но только на один уровень глубиной. Я понятия не имею, как это будет работать.

Хотя я полагаю, что вы могли бы использовать тот же трюк locError, чтобы сделать locFromJust - но вы хотели общий путь, а это не так.

...