Haskell — распечатать трассировку после выполнения

У меня есть проект для Uni по написанию компилятора (на Haskell) для простого выдуманного императивного языка. Одним из требований является печать операторов отладки при входе в вызов функции, выходе из функции и назначении переменных.

Печатать сообщения при входе в функции легко, я просто использую Debug.trace, например:

functionValue = trace "Entering function" (evaluateFunction functionArguments)

Тот же процесс применяется при назначении переменных. Чего я не могу понять, так это того, как печатать при возврате из вызова функции и правильно синхронизировать вывод с другими выходами. Каждая попытка, которую я предпринял до сих пор, приводила к тому, что «Функция выхода» печаталась сразу после «Входа в функцию» - мне нужно, чтобы внутренние операторы отладки функции (назначение и вложенные вызовы функций) были напечатаны до того, как «Функция выхода» будет напечатана.

Мои императивные привычки говорят мне, что мне нужен способ принудительного выполнения (evaluateFunction functionArguments) до вывода функции выхода, но это кажется невозможным и неправильным в Haskell.

Пример вывода, который я получаю сейчас:

Entering main function...
Leaving main function...
Entering fn1 function...
Leaving fn1 function...
Assigning value1 to A.
Assigning value2 to C.
Entering fn2 function...
Leaving fn2 function...
Assigning value3 to B.
Assigning value4 to C.

Вывод той же программы так, как мне нужно:

Entering main function...
Entering fn1 function...
Assigning value1 to A.
Leaving fn1 function...
Assigning value2 to C.
Entering fn2 function...
Assigning value3 to B.
Assigning value4 to C.
Leaving fn2 function...
Leaving main function...

Итак, что такое идиома Haskell для «запустить myFunctionWithTraces, затем распечатать myString»?


person drodgers    schedule 15.04.2012    source источник
comment
Поскольку вам необходимо гарантировать, что сообщения печатаются в определенном порядке, вам не следует использовать Debug.Trace. Вместо этого стисните зубы, запустите IO и используйте putStrLn или подобное.   -  person dave4420    schedule 15.04.2012
comment
Вы компилируете на Haskell?   -  person n. 1.8e9-where's-my-share m.    schedule 15.04.2012


Ответы (2)


Если вы хотите немедленно распечатать трассировку, вы можете поднять функцию до монады IO и поместить ее между двумя putStr, например

trace :: String -> IO () -> IO ()
trace name f = do
    putStrLn $ "Entering " ++ name
    f
    putStrLn $ "Leaving " ++ name

А потом:

main = trace "main" $ do
    fn1
    fn2

fn1 = trace "fn1" $ do
    return ()

fn2 = trace "fn2" $ do
    return ()

Это также можно сделать чисто, с помощью Writer монада (т.е. не выводить, а просто накапливать отладочный вывод по ходу работы). trace будет выглядеть примерно так:

trace :: String -> Writer String () -> Writer String ()
trace name f = do
    tell $ "Entering " ++ name ++ "\n"
    f
    tell $ "Leaving " ++ name ++ "\n"

и с дополнительным шагом развертывания вывода отладки с помощью runWriter или execWriter.

Изменить: обобщить trace до IO a не так уж сложно:

trace :: String -> IO a -> IO a
trace name f = do
    putStrLn $ "Entering " ++ name
    ret <- f
    putStrLn $ "Leaving " ++ name
    return ret

main = trace "main" $ do
    a <- fn1
    b <- fn2
    print $ a + b

fn1 = trace "fn1" $ do
    return 42

fn2 = trace "fn2" $ do
    return 69
person Cat Plus Plus    schedule 15.04.2012

[Код в комментарии был нечитаемым, поэтому я опубликовал еще один ответ в качестве комментария к Cat Plus Plus]

Наконец-то я пропустил монаду IO через весь свой код, но это решение не совсем работает — мне нужно получить возвращаемые значения (т. е. IO (значение)) из моих функций.

do
    putStrLn $ "Entering " ++ name
    f
    putStrLn $ "Leaving " ++ name

Приведенный выше фрагмент вернет IO() (пустую монаду IO). Поэтому я изменил его так:

do
    putStrLn $ "Entering " ++ name
    returnVal <- f
    putStrLn $ "Leaving " ++ name
    return (returnVal)

Но теперь это печатается:

действия внутри функции... ... ... Вход в функцию Выход из функции

Изменить: неверный вывод был получен по моей вине: я случайно поставил result <- f before putStrLn "Entering...

person drodgers    schedule 22.04.2012