Programing

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

lottogame 2020. 9. 14. 21:35
반응형

하스켈에서 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) }

You can declare a monad instance for this, and then write the function

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

which simply logs its input. Now suppose you want a monad that has logging capabilities, but also does something else - say it can read from an environment too. You'd implement this as

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

Now because the writer is inside the ReaderT monad transformer, if you want to log output you can't use tell w (because that only operates with unwrapped writers) but you have to use lift $ tell w, which "lifts" the tell function through the ReaderT so that it can access the inner writer monad. If you wanted two layers transformers (say you wanted to add error handling as well) then you'd need to use lift $ lift $ tell w. This quickly gets unwieldy.

Instead, by defining a type class we can make any monad transformer wrapper around a writer into an instance of writer itself. For example,

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

that is, if w is a monoid, and m is a MonadWriter w, then ReaderT r m is also a MonadWriter w. This means that we can use the tell function directly on the transformed monad, without having to bother with explicitly lifting it through the monad transformer.


A function called "writer" is made available in lieu of a "Writer" constructor. Change:

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

to:

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


I got a similar message from trying the LYAH "For a few Monads More" using the online Haskell editor in repl.it

I changed the import from:

import Control.Monad.Writer

to:

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

So my code now works looking like this (with inspiration from Kwang's Haskell Blog):

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) 

Code is currently runable here

참고URL : https://stackoverflow.com/questions/11684321/how-to-play-with-control-monad-writer-in-haskell

반응형