【해답 예】Haskell 모나드 변환자 초입문

【해답 예】Haskell 모나드 변환자 초입문

2022-10-04 last update

17 minutes reading 하스켈
Haskell 모나드 변환기 초입문 의 해답 예입니다.

State 모나드



【문 1】State 모나드를 다루는 returnStateT 를 사용해, runStaterunStateT
import Control.Monad.Identity
import Control.Monad.State hiding (return, runState)

return' x = StateT $ \s -> Identity (x, s)
runState' st = runIdentity . runStateT st

main = do
    let st = return' 1
    print $ runState' st ()

실행 결과
(1,())

Identity 모나드가 끼여 있음을 의식합니다.

StateT 모나드 변환기



재구성



【문 2】StateT 모나드 변환자를 다루는 bind , get , modify , lift 를 구현해 주세요. do 는 사용하지 마십시오. >>= 는 StateT 모나드 변환자 이외에만 사용하십시오.
import Control.Monad
import Control.Monad.State hiding (get, modify, lift)

a `bind` b = StateT $ \s ->
    runStateT  a    s >>= \(r, s1) ->
    runStateT (b r) s1

get = StateT $ \s -> return (s, s)
modify f = StateT $ \s -> return ((), f s)
lift m = StateT $ \s -> m >>= \a -> return (a, s)

fact x = (`execStateT` 1) $
    forM_ [1..x] $ \i ->
        modify (* i) `bind` \_ ->
        get `bind` \v ->
        lift $ putStrLn $ "*" ++ show i ++ " -> " ++ show v

main = fact 5 >>= print

실행 결과
*1 -> 1
*2 -> 2
*3 -> 6
*4 -> 24
*5 -> 120
()

다시 작성



【문 3】문 2의 factdo<- 질문 2에서 다시 구현한 함수는 사용하지 마십시오.
import Control.Monad
import Control.Monad.State

fact x = (`execStateT` 1) $ do
    forM_ [1..x] $ \i -> do
        modify (* i)
        v <- get
        lift $ putStrLn $ "*" ++ show i ++ " -> " ++ show v

main = fact 5 >>= print

실행 결과
*1 -> 1
*2 -> 2
*3 -> 6
*4 -> 24
*5 -> 120
()

모나드 변환기로 합성



Maybe 모나드



【질문 4】다음의 코드는 종류를 판별하면서 캐릭터 라인을 읽어 진행하는 것을 의도하고 있습니다. Maybe 모나드를 사용하여 오류가 발생하지 않도록 수정합니다. 모나드 변환자는 사용하지 마십시오.
import Data.Char

getch f (x:xs) | f x = Just (x, xs)
getch _ _            = Nothing

test s0 = do
    (ch1, s1) <- getch isUpper s0
    (ch2, s2) <- getch isLower s1
    (ch3, s3) <- getch isDigit s2
    return [ch1, ch2, ch3]

main = do
    print $ test "Aa0"
    print $ test "abc"
Just "Aa0"
Nothing

StateT 모나드 변환기



【문5】문4의 해답을 StateT 모나드 변환자를 사용해 재작성해 주세요.
import Data.Char
import Control.Monad.State

getch f = StateT getch where
    getch (x:xs) | f x = Just (x, xs)
    getch _            = Nothing

test = evalStateT $ do
    ch1 <- getch isUpper
    ch2 <- getch isLower
    ch3 <- getch isDigit
    return [ch1, ch2, ch3]

main = do
    print $ test "Aa0"
    print $ test "abc"
Just "Aa0"
Nothing

다른 모나드 변환기



【질문 6】다음 코드의 printmain 에서 각 test* 실행 결과가 동일하도록 조정하십시오.
import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.List

testR x = (`runReaderT` x) $ do
    a <- ask
    lift $ print $ a + 1

testW x = runWriterT $ do
    tell $ show x
    lift $ print $ x + 1

testL x = runListT $ do
    lift $ print [x + 1]

main = do
    testR 0
    testW 0
    testL 0

실행 결과
1
1
[1]

모나드 변환기의 평가 결과가 IO 모나드에서 반환되기 때문에 do 에 직접 기술할 수 있습니다.

liftIO



【문 7】문 6을 main로 풀어 주십시오.
import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.List

testR x = (`runReaderT` x) $ do
    a <- ask
    liftIO $ print $ a + 1

testW x = runWriterT $ do
    tell $ show x
    liftIO $ print $ x + 1

testL x = runListT $ do
    liftIO $ print [x + 1]

main = do
    testR 0
    testW 0
    testL 0

실행 결과
1
1
[1]

liftM



【문8】 다음의 리스트내포 표기를 liftIO 계의 함수로 재작성해 주세요.
import Control.Monad

main = do
    print $ liftM2 (,) [0, 1] [0, 2]
    print $ liftM2 (+) [0, 1] [0, 2]

실행 결과
[(0,0),(0,2),(1,0),(1,2)]
[0,2,1,3]
liftM 그냥 움직임을 알기 어렵기 때문에 (+) 와 비교해 보세요.