Skip to content

Instantly share code, notes, and snippets.

@nattybear
Last active June 28, 2022 12:48
Show Gist options
  • Save nattybear/2ca95a74c8703516542bb5557d672238 to your computer and use it in GitHub Desktop.
Save nattybear/2ca95a74c8703516542bb5557d672238 to your computer and use it in GitHub Desktop.
하스켈 Maybe 트랜스포머

이 글은 위키북스 하스켈을 읽고 정리한 것이다.

두가지 모나드가 필요할 때

사용자에게 문자열을 입력 받아서 원하는 조건에 부합하는지 검사하려는 코드를 하나의 함수로 구현한다고 하자. 아래와 같이 두가지 타입이 필요하다.

  • 사용자에게 뭔가 입력을 받는다. 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...")

대문 링크

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment