2012-08-07 24 views
8

Deseo hacer un seguimiento de los cambios en la mónada de estado. Esto no funciona:¿Cómo uso Debug.Trace.trace en la mónada de estado?

main :: IO() 
main = do 
    print $ snd $ execState compute initialState 

traceThis :: (Show a) => a -> a 
traceThis x = trace ("test: " ++ show x) x 

compute :: State ([Row], Integer) String 
compute = liftM traceThis $ get >>= \(rs, result) -> put (rs, result + 3) >> return "foo" 

Nada se imprime (excepto el resultado final de la impresión en la función principal, que ha sido correctamente actualizada).

¿Alguna idea o alternativa para rastrear el estado? Quiero usar esto para verificar la corrección de una solución de proyecto euler.

Respuesta

11

El problema en su caso es que traceThis nunca se evalúa. Haskell es un lenguaje vago, por lo que solo evalúa las expresiones que se necesitan. Y dado que no evalúa el resultado del cálculo, solo el estado, no es necesario evaluar traceThis dentro de compute. Si imprime por ejemplo

print $ evalState compute initialState 

entonces el valor del resultado del cómputo con estado se evalúa junto con llamar traceThis.

Una mejor opción sería la de definir una función monádica que las fuerzas de imprimir el valor del resultado cada vez que se evalúa cualquier parte del cálculo monádico:

traceState :: (Show a) => a -> State s a 
traceState x = state (\s -> trace ("test: " ++ show x) (x, s)) 

compute :: State ([Int], Integer) String 
compute = get >>= \(rs, result) -> put (rs, result + 3) 
       >> return "foo" 
       >>= traceState 

Actualización: Esto se puede generalizar a una mónada arbitraria. El punto principal es que trace debe envolver el cómputo monádico, no sólo el valor en el interior, por lo que se evalúa cuando se evalúa >>=, independientemente de si el valor en el interior se evalúa o no:

traceMonad :: (Show a, Monad m) => a -> m a 
traceMonad x = trace ("test: " ++ show x) (return x) 
+0

¡Muy bonito! Una vez que ya no necesito iniciar sesión, puedo desactivarlo eliminando una sola expresión. – somesoaccount

+0

@somesoaccount Se actualizó la respuesta con una solución más general. –

4

Aquí hay una alternativa. Utilice StateT s IO como su mónada:

compute :: StateT ([Row], Integer) IO String 
compute = do 
    (rs, result) <- get 
    lift $ putStrLn "result = " ++ show result 
    put (rs, result + 3) 
    return "foo" 

ya se puede intercalar IO acciones en cualquier lugar utilizando lift.

Para obtener más información sobre los transformadores de mónada, le recomiendo que lea la excelente introducción: Monad Transformers - Step by Step.

+0

Esto parece como un simple solución, el único culpable para mí es el uso de transformadores de mónada, ya que escuché que son lentos y necesito un buen rendimiento aquí. – somesoaccount

+1

Ya está pagando por el transformador de mónada, ya sea que se dé cuenta o no.'State s' es solo un sinónimo de tipo 'StateT s Identity'. [Prueba] (http://hackage.haskell.org/packages/archive/transformers/0.3.0.0/doc/html/Control-Monad-Trans-State-Lazy.html#t:State). Además, la mayoría de las preocupaciones sobre el rendimiento del transformador monad son exageradas. Te recomiendo que lo compares tú mismo y lo compares con el rendimiento de 'Debug.Trace' antes de descartarlo. –

4

Cuando llama al execState, solo está solicitando el estado final, no el valor devuelto por la función compute. liftM, por otro lado, eleva su función traceThis a una acción en la mónada State que no toca el estado. Por lo tanto, debido a la pereza, solo se llamará traceThis si fuerza el valor devuelto por compute para ser evaluado. En general, para que trace funcione correctamente, debe asegurarse de que se evalúa el valor que usted llama.

Debug.Trace generalmente solo es adecuado para la depuración rápida; no es un sistema de registro muy poderoso y puede ser difícil de usar debido a la pereza. Si está buscando una forma de hacerlo de forma más robusta, puede agregar otro elemento (tal vez una lista de cadenas) a su tupla de estado y tener la función compute escribir mensajes de registro de escritura a eso.

+0

Gracias por su explicación. También pensé en la pereza, pero como el resultado se imprimió, descarté esta posibilidad ... No estoy seguro de si me gustaría agregar un registro extenso, ya que se eliminará en el futuro por razones de rendimiento. – somesoaccount

Cuestiones relacionadas