[haskell] 하스켈에서 Control.Monad.Writer를 사용하는 방법?

저는 함수형 프로그래밍을 처음 접했고 최근에는 Learn You a Haskell 에서 배웠지 만이 장을 살펴 보았을 때 아래 프로그램을 고수했습니다.

import Control.Monad.Writer

logNumber :: Int -> Writer [String] Int
logNumber x = Writer (x, ["Got number: " ++ show x])

multWithLog :: Writer [String] Int
multWithLog = do
    a <- logNumber 3
    b <- logNumber 5
    return (a*b)

이 줄을 .hs 파일에 저장했지만 불평 한 내 ghci로 가져 오지 못했습니다.

more1.hs:4:15:
    Not in scope: data constructor `Writer'
    Perhaps you meant `WriterT' (imported from Control.Monad.Writer)
Failed, modules loaded: none.

“: info”명령으로 유형을 조사했습니다.

Prelude Control.Monad.Writer> :info Writer
type Writer w = WriterT w Data.Functor.Identity.Identity
               -- Defined in `Control.Monad.Trans.Writer.Lazy'

내 관점에서 이것은 “newtype Writer wa …”와 같은 것으로되어 있었기 때문에 데이터 생성자를 공급하고 Writer를 얻는 방법에 대해 혼란 스럽습니다.

버전 관련 문제 일 수 있으며 ghci 버전은 7.4.1입니다.



답변

패키지 Control.Monad.Writer는 데이터 생성자를 내 보내지 않습니다 Writer. LYAH가 쓰여졌을 때 이것이 달랐던 것 같아요.

ghci에서 MonadWriter 유형 클래스 사용

대신 writer함수를 사용하여 작성자를 만듭니다 . 예를 들어, ghci 세션에서

ghci> import Control.Monad.Writer
ghci> let logNumber x = writer (x, ["Got number: " ++ show x])

이제 logNumber작가를 만드는 함수입니다. 유형을 요청할 수 있습니다.

ghci> :t logNumber
logNumber :: (Show a, MonadWriter [String] m) => a -> m a

이것은 추론 된 유형이 특정 작성자 를 반환하는 함수가 아니라 MonadWriter유형 클래스 를 구현하는 모든 것을 의미합니다 . 이제 사용할 수 있습니다.

ghci> let multWithLog = do { a <- logNumber 3; b <- logNumber 5; return (a*b) }
    :: Writer [String] Int

(실제로 한 줄에 모두 입력 된 입력). 여기에서 유형을 multWithLog로 지정했습니다 Writer [String] Int. 이제 실행할 수 있습니다.

ghci> runWriter multWithLog
(15, ["Got number: 3","Got number: 5"])

그리고 모든 중간 작업을 기록하는 것을 볼 수 있습니다.

코드가 이렇게 작성된 이유는 무엇입니까?

MonadWriter유형 클래스 를 생성해야하는 이유는 무엇 입니까? 그 이유는 모나드 변환기와 관련이 있습니다. 올바르게 인식했듯이 구현하는 가장 간단한 방법 Writer은 쌍 위에 새로운 유형 래퍼를 사용하는 것입니다.

newtype Writer w a = Writer { runWriter :: (a,w) }

이에 대한 모나드 인스턴스를 선언 한 다음 함수를 작성할 수 있습니다.

tell :: Monoid w => w -> Writer w ()

단순히 입력을 기록합니다. 이제 로깅 기능이 있지만 다른 작업도 수행하는 모나드를 원한다고 가정합니다. 환경에서도 읽을 수 있다고 가정합니다. 이것을 다음과 같이 구현할 것입니다.

type RW r w a = ReaderT r (Writer w a)

이제 작가는 내부에 있기 때문에 ReaderT당신이 출력은 사용할 수 없습니다 기록 할 경우, 모나드 변압기 tell w(즉, 만 풀어 작가와 함께 작동하기 때문에)을하지만 당신은 사용해야 lift $ tell w하는 “리프트”를 tell관통 기능을 ReaderT가 액세스 할 수 있도록 내부 작가 모나드. 두 개의 레이어 변환기를 원하면 (예를 들어 오류 처리도 추가하고 싶음) lift $ lift $ tell w. 이것은 빠르게 다루기 어려워집니다.

대신, 타입 클래스를 정의함으로써 라이터를 둘러싼 모나드 변환기 래퍼를 라이터 자체의 인스턴스로 만들 수 있습니다. 예를 들면

instance (Monoid w, MonadWriter w m) => MonadWriter w (ReaderT r m)

경우 즉, w모노 이드이고, mA는 MonadWriter w그 후, ReaderT r m도이다 MonadWriter w. 이것은 우리가 tell변환 된 모나드에서 직접 함수를 사용할 수 있음을 의미합니다 . 모나드 변환기를 통해 명시 적으로 해제하지 않아도됩니다.


답변

“작성기”생성자 대신 “작성기”라는 함수를 사용할 수 있습니다. 변화:

logNumber x = Writer (x, ["Got number: " ++ show x])

에:

logNumber x = writer (x, ["Got number: " ++ show x])


답변

나는 LYAH을 시도하는 유사한 메시지를 받았습니다 “몇 가지 더 모나드를 들어” 의 온라인 하스켈 편집기를 사용하여 repl.it을

가져 오기를 다음에서 변경했습니다.

import Control.Monad.Writer

에:

import qualified Control.Monad.Trans.Writer.Lazy as W

이제 내 코드는 다음과 같이 작동합니다 ( Kwang의 Haskell 블로그 에서 영감을 얻음 ).

import Data.Monoid
import qualified Control.Monad.Trans.Writer.Lazy as W


output :: String -> W.Writer [String] ()
output x = W.tell [x]


gcd' :: Int -> Int -> W.Writer [String] Int
gcd' a b
    | b == 0 = do
        output ("Finished with " ++ show a)
        return a
    | otherwise = do
        output (show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b))
        gcd' b (a `mod` b)

main :: IO()
main = mapM_ putStrLn $ snd $ W.runWriter (gcd' 8 3) 

코드는 현재 여기에서 실행할 수 있습니다.


답변