Pergunta

Eu gostaria de saber como a depuração é conseguido em uma linguagem funcional preguiçoso.
você pode usar pontos de interrupção, instruções de impressão e técnicas tradicionais? É este mesmo uma boa idéia?
É meu entendimento que a programação funcional pura não permite efeitos colaterais, com exceção de mônadas.
Ordem de execução também não é garantida.
Você tem que programar uma mônada para cada seção do código que você deseja teste? Eu gostaria alguns insights sobre esta pergunta de alguém mais experiente nesta área.

Foi útil?

Solução

Nada impede que você use pontos de interrupção em um programa funcional preguiçosamente avaliados. A diferença para avaliação ansiosa é quando o programa irá parar no ponto de interrupção e que o traço será semelhante. O programa irá parar quando a expressão de um ponto de interrupção é definido em que realmente está sendo reduzida (obviamente).

Em vez do rastreamento de pilha que você está acostumado a você obter as reduções que levaram à redução da expressão com o ponto de interrupção do mesmo.

pequeno exemplo bobo. Você tem este programa Haskell.

add_two x = 2 + x

times_two x = 2 * x

foo = times_two (add_two 42)

E você colocar um ponto de interrupção na primeira linha (add_two), em seguida, avaliar foo. Quando o programa pára no ponto de interrupção, em uma linguagem ansioso que você esperaria ter um traço como

add_two
foo

e times_two ainda nem começou a ser avaliado, mas no GHCi depurador você começa

-1  : foo (debug.hs:5:17-26)
-2  : times_two (debug.hs:3:14-18)
-3  : times_two (debug.hs:3:0-18)
-4  : foo (debug.hs:5:6-27)
<end of history>

que é a lista de reduções que levou à redução da expressão você coloca o ponto de interrupção. Note-se que ele se parece com times_two "chamado" foo mesmo que ele não fazê-lo explicitamente. É possível ver a partir disto que a avaliação de 2 * x em times_two (-2) fez forçar a avaliação de (add_two 42) (-1) a partir da linha foo. De lá você pode executar um passo como em um depurador imperativo (executar a próxima redução).

Outra diferença para a depuração em uma linguagem ansioso é que as variáveis ??podem ser thunks ainda não avaliados. Por exemplo, no passo -2 no rastreamento acima e inspecionar x, você vai encontrar ainda é uma conversão unevaluated (indicado por colchetes em GHCi).

Para informações e exemplos muito mais detalhada (como passo através do traço, inspecionar valores, ...), veja seção gHCi Debugger no manual do GHC. Há também a Leksah IDE que eu não tenha ainda utilizado como eu sou um VIM e do usuário terminal, mas tem um interface gráfica para o GHCi depurador de acordo com o manual.

Você também pediu instruções de impressão. Apenas com funções puras, isto não é tão facilmente possível, como uma declaração de impressão teria que estar dentro da mônada IO. Assim, você tem uma função puro

foo :: Int -> Int

e deseja adicionar uma instrução de rastreamento, a impressão voltaria uma ação na mônada IO e por isso você tem que ajustar a assinatura da função que você deseja colocar essa instrução trace, e as assinaturas das funções que chamá-lo, ...

Esta não é uma boa idéia. Então, você precisa de alguma maneira de pureza pausa para alcançar instruções de rastreamento. Em Haskell, isso pode ser feito com unsafePerformIO. Há a Debug.Trace módulo que já tem uma função

trace :: String -> a -> a

que gera a seqüência e retorna o segundo parâmetro. Seria impossível escrever como uma função pura (bem, se você pretende realmente de saída da cadeia, que é). Ele usa unsafePerformIO sob o capô. Você pode colocar isso em uma função pura para a saída de uma impressão vestígios.

Você tem que programar uma mônada para cada seção do código que você deseja teste?

Eu sugiro antes pelo contrário, fazer tantas funções puras quanto possível (estou assumindo aqui que você quer dizer o IO mônada para imprimir, mônadas não são necessariamente impura). avaliação preguiçosa permite separar código IO do processamento de código muito limpa.

se as técnicas de depuração imperativo são uma boa idéia ou não depende da situação (como sempre). Acho testando com QuickCheck / SmallCheck muito mais útil do que o teste de unidade em linguagens imperativas, então eu ir por esse caminho primeiro para evitar tanto quanto possível a depuração. Propriedades QuickCheck agirually fazer especificações de função concisa agradáveis ??(um monte de código de teste em linguagens imperativas parece apenas uma outra bolha de código para me).

Um truque para evitar um monte de depuração é decompor a função em muitas subfunções menores e teste como muitos deles quanto possível. Este pode ser um unusal pouco quando proveniente de programação imperativa, mas é um bom hábito não importa o idioma que você está usando.

Então, novamente, a depuração! = Testando e se algo der errado em algum lugar, breakpoints e traços pode ajudá-lo.

Outras dicas

Eu não acho que este tema pode ser tratado dentro de um curto espaço. Por favor, leia os jornais disponíveis nos seguintes links:

  1. A Theory of Tracing programas funcionais Pure .
  2. As publicações Haskell Tracer .
  3. Haskell depuração Technologies .

Eu nunca mergulhou em nada terrivelmente complicado em Haskell, mas o fato de que os efeitos colaterais são praticamente desapareceu eliminou a maior parte da necessidade de depuração. funções puras são extremamente simples para testar e verificar sem um depurador.

Por outro lado, eu fiz experiência um par de vezes que eu precisava para depuração algo dentro de uma mônada, caso em que eu já era capaz de imprimir / log / whatever.

Pelo menos para programas menores ou sistemas de depuração tipo de sai pela janela. tipagem forte e tipo de verificação estática realmente eliminar ainda mais os erros tradicionais que você encontra em programação procedural. A maioria dos erros, se houver, são erros lógicos (chamadas as funções erradas, erro matemático, etc) - muito fácil de testar interativamente

.

A partir da experiência com Clojure (que é preguiçoso, funcional, e incentiva, mas não impõe pureza):

  • Você pode definir pontos de interrupção assim como com qualquer outra língua. No entanto, becuase de avaliação preguiçosa, estes podem não ser chamado imediatamente, mas vai ser atingido assim que a avaliação da estrutura preguiçoso é forçado.

  • Em linguagens funcionais preguiçosas que permitem efeitos colaterais (Clojure incluído) você pode inserir printlns e outro registo de depuração de forma relativamente fácil. Eu, pessoalmente encontrá-los muito útil. Você tem que ser cuidadoso sobre quando estes são chamados por causa da preguiça, mas se você não ver a saída em tudo o que pode ser um indício de que o código não está sendo avaliada por causa da preguiça .....

Tendo dito tudo o que precede, eu nunca até agora precisava de recorrer para o depurador. Muitas vezes alguns testes simples (talvez no REPL) são suficientes para verificar que código funcional está funcionando corretamente, e se estes falharem, então é geralmente bastante óbvio o que está acontecendo de errado.

Permita-me para anunciar uma ferramenta de meu próprio para depurar problemas de preguiça. Ele me ajudou a resolver em uma hora um vazamento de memória preguiça-relacionados que eu já passei 2 dias de depuração.

http://www.haskell.org/pipermail/ Haskell-café / 2012-janeiro / 098847.html

http://hackage.haskell.org/package/htrace

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top