IT Share you

printfs로 Haskell을 "디버그"하는 방법?

shareyou 2020. 12. 1. 20:02
반응형

printfs로 Haskell을 "디버그"하는 방법?


Ocaml 커뮤니티에서 왔기 때문에 Haskell을 조금 배우려고합니다. 전환은 아주 잘 진행되지만 디버깅과 약간 혼동됩니다. 나는 내 ocaml 코드에 (많은) "printf"를 넣거나, 중간 값을 검사하거나, 계산이 정확히 어디에서 실패했는지 확인하기 위해 플래그로 사용했습니다.

printf가 IO 액션 이기 때문에 이런 종류의 디버깅을 할 수 있으려면 IO 모나드 안에있는 모든 하스켈 코드를 들어야 합니까? 아니면 더 좋은 방법이 있습니까 (피할 수 있다면 손으로하고 싶지 않습니다)

나는 또한 추적 기능을 찾습니다 : http://www.haskell.org/haskellwiki/Debugging#Printf_and_friends 정확히 내가 원하는 것처럼 보이지만 유형을 이해하지 못합니다. 어디에 IO 가 없습니다 ! 누군가 추적 기능의 동작을 설명 할 수 있습니까?


trace디버깅에 가장 사용하기 쉬운 방법입니다. IO당신이 지적한 이유 가 아닙니다 IO. 모나드 에서 코드를 들어 올릴 필요가 없습니다 . 다음과 같이 구현됩니다.

trace :: String -> a -> a
trace string expr = unsafePerformIO $ do
    putTraceMsg string
    return expr

따라서 장면 뒤에 IO가 있지만 unsafePerformIO탈출하는 데 사용됩니다. 유형 IO a -> a과 이름을 보면 추측 할 수있는 참조 투명성을 잠재적으로 깨뜨리는 함수입니다 .


trace단순히 불결하게됩니다. IO모나드 의 요점은 순도를 유지하고 (타입 시스템에 의해 인식되지 않는 IO 없음) 명령문의 실행 순서를 정의하는 것입니다. 그렇지 않으면 지연 평가를 통해 실제로 정의되지 않습니다.

그러나 위험을 감수하면서 일부를 함께 해킹 할 수 있습니다 IO a -> a. 즉, 불순한 IO를 수행 할 수 있습니다 . 이것은 해킹이며 당연히 게으른 평가에서 "충분하다"지만 추적이 단순히 디버깅을 위해 수행하는 작업입니다.

그럼에도 불구하고 디버깅을 위해 다른 방법을 사용해야 할 것입니다.

  1. 중간 값 디버깅의 필요성 감소

    • 정확성이 명백한 작고 재사용 가능하며 명확하고 일반적인 함수를 작성하십시오.
    • 올바른 조각을 더 정확한 조각으로 결합하십시오.
    • 테스트를 작성 하거나 대화식으로 조각을 시도해보세요.
  2. 중단 점 등 사용 (컴파일러 기반 디버깅)

  3. 일반 모나드를 사용하십시오. 그럼에도 불구하고 코드가 모나 딕이라면 구체적인 모나드와 독립적으로 작성하십시오. type M a = ...일반 대신 사용하십시오 IO .... 나중에 변환기를 통해 쉽게 모나드를 결합하고 그 위에 디버깅 모나드를 넣을 수 있습니다. 모나드가 필요하지 않더라도 Identity a순수한 값을 삽입 할 수 있습니다.


그만한 가치가있는 것은 실제로 여기에서 두 가지 종류의 "디버깅"이 문제가됩니다.

  • 특정 하위 표현식이 각 호출에 대해 갖는 값과 같은 중간 값을 재귀 함수에 로깅
  • 표현식 평가의 런타임 동작 검사

엄격한 명령형 언어에서는 일반적으로 일치합니다. Haskell에서 그들은 종종 다음을 수행하지 않습니다.

  • 중간 값을 기록하면 버려 질 용어를 강제로 평가하는 것과 같이 런타임 동작을 변경할 수 있습니다.
  • 실제 계산 프로세스는 게으름과 공유 된 하위 표현식으로 인해 표현식의 외관상 구조와 크게 다를 수 있습니다.

중간 값의 로그를 유지하려면 여러 가지 방법이 있습니다. 예를 들어 모든 것을으로 들어 올리는 대신 IO간단한 Writer모나드로 충분합니다. 이것은 함수가 실제 값의 2- 튜플을 반환하는 것과 같습니다. 결과 및 누산기 값 (일반적으로 일종의 목록).

또한 일반적으로 모든 것을 모나드 에 넣을 필요는 없습니다 . "log"값에 기록해야하는 함수 만-예를 들어 로깅을 수행해야 할 수있는 하위 표현식 만 빼내어 메인 로직을 순수하게 남겨 둘 수 있습니다. 그런 다음 순수한 함수를 결합하고 일반적인 방식으로 fmaps 및 기타를 사용하여 계산을 로깅하여 전체 계산을 재 조립합니다 . Writer이것은 모나드에 대한 일종의 미안한 변명 이라는 점을 명심하십시오 . 로그 에서 읽을 방법이없고 쓰기 만 가능하며, 각 계산은 컨텍스트와 논리적으로 독립적이므로 작업을 쉽게 처리 할 수 ​​있습니다.

그러나 어떤 경우에는 그것이 과도합니다. 많은 순수 함수의 경우 하위 표현식을 최상위 수준으로 이동하고 REPL에서 작업을 시도하는 것이 매우 잘 작동합니다.

당신이 원하는 경우 실제로 그러나, 순수 코드의 런타임 동작을 검사하기 - 서브 표현식의 발산 이유를 예를 들어 알아 내기 위해 - 일반적으로이 다른 순수 코드에서 그렇게 할 수없는 방식으로 사실 .. 이대로, 이것은 본질적으로 순결 정의 . 따라서이 경우 순수한 언어 "외부에"존재하는 도구를 사용할 수밖에 없습니다. unsafePerformPrintfDebugging--errr 과 같은 불순한 함수 trace또는 GHCi 디버거와 같은 수정 된 런타임 환경입니다.


trace 또한 인쇄에 대한 주장을 과도하게 평가하여 그 과정에서 게으름의 많은 이점을 잃는 경향이 있습니다.


출력을 연구하기 전에 프로그램이 끝날 때까지 기다릴 수 있다면 Writer 모나드 를 쌓는 것이 로거를 구현하는 고전적인 접근 방식입니다. 여기이것을 사용 하여 불순한 HDBC 코드에서 결과 집합을 반환합니다.


글쎄, 전체 Haskell은 지연 평가의 원칙을 기반으로 구축 되었기 때문에 (계산 순서는 실제로 비 결정적이므로) printf의 사용은 거의 의미가 없습니다.

REPL + inspect 결과 값이 실제로 디버깅에 충분하지 않은 경우 모든 것을 IO로 래핑하는 것이 유일한 선택입니다 (그러나 Haskell 프로그래밍의 올바른 방법은 아닙니다).

참고 URL : https://stackoverflow.com/questions/3546592/how-to-debug-haskell-with-printfs

반응형