이 글은 위키북스 하스켈을 읽고 정리한 것이다.
사용자에게 문자열을 입력 받아서 원하는 조건에 부합하는지 검사하려는 코드를 하나의 함수로 구현한다고 하자. 아래와 같이 두가지 타입이 필요하다.
- 사용자에게 뭔가 입력을 받는다.
IO
- 입력 받은 문자열이 유효하지 않을 수 있다.
Maybe
아래 함수 isValid
는 여러 조건을 모두 만족해야만 참을 리턴한다.
isValid :: String -> Bool
isValid s = length s >= 8
&& any isAlpha s
&& any isNumber s
&& any isPunctuation s
아래 함수 getPassphrase
는 사용자에게 비밀번호를 입력 받고 검사한 뒤 결과를 리턴한다. 입력 받은 비밀번호가 조건에 부합하지 않을 수도 있다. 전체 do 블록이 IO
모나드이기 때문에 Maybe
모나드를 직접 연결하지는 못하고 결국 return
을 할 수 밖에 없다.
getPassphrase :: IO (Maybe String)
getPassphrase = do
s <- getLine
if isValid s
then return (Just s)
else return Nothing
아래 함수 askPassphrase
에서는 IO
모나드 안에서 Maybe
모나드의 결과를 사용하기 위해 어쩔 수 없이 패턴 매칭을 사용했다.
askPassphrase :: IO ()
askPassphrase = do
putStrLn "Insert your new passphrase:"
maybe_value <- getPassphrase
case maybe_value of
Just value -> do
putStrLn "Storing in database..."
-- do stuff
Nothing -> putStrLn "Passphrase invalid."
IO
모나드이면서 동시에 Maybe
모나드의 성질도 갖는 것을 모나드 트랜스포머라고 한다. monad transformer
여기서는 MaybeT
라고 부르는데 보통 트랜스포머는 이름 뒤에 알파벳 대문자 T
를 붙인다.
MaybeT
는 타입 m (Maybe a)
를 감싸는 wrapper이고 m
에는 아무 모나드나 올 수 있다.
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
모나드 트랜스포머는 모나드를 모나드로 변환시키는 것 이다. 결국 그 자체로 모나드이기 때문에 아래와 같이 모나드와 관련 있는 타입 클래스들의 인스턴스를 모두 구현할 수 있다.
Monad
Applicative
Functor
Alternative
MonadPlus
MonadTrans
모나드 트랜스포머를 이용하면 위에서 소개했던 비밀번호 입력 코드를 더 간편하게 쓸 수 있다.
특히 트랜스포머를 사용하지 않았을 때는 maybe_value
를 패턴 매칭할 수 밖에 없었는데 이제는 그러지 않아도 된다.
getPassphrase :: MaybeT IO String
getPassphrase = do
s <- lift getLine
guard (isValid s)
return s
askPassphrase :: MaybeT IO ()
askPassphrase = do
lift (putStrLn "Insert your new passphrase:")
value <- getPassphrase
lift (putStrLn "Storing in database...")