Haskell Monad(모나드)란?

17721 단어 하스켈

요약



  • Functor: 함수는 권외에 있지만 인수의 값이 권내이면 <$> (fmap) 를 사용할 수 있다.

  • Applicative : 함수도 인수의 값도 같은 권내이면 <*> 를 사용할 수 있다. functor 클래스형권내에 값(함수)을 소속시키기 위해서는 pure 를 사용할 수 있다.

  • Monad:메소드 체인등에서 반환값은 권내이지만, 함수는 인수의 값이 권외에 있을 때에는 >>= 를 사용할 수 있다. return 는 값을 monad 클래스형권내에 소속시킬 때 사용한다.

  • 상세



    Monad를 이해하기 위해서는 Haskell에 대해 여기까지 배운 것을 총동원할 필요가 있다고 느꼈기 때문에 그들도 김에 정리.

    간단한 유형


    カインド(種):*
    개념도


    기능



    형을 인수에 취해 어떠한 처리를 행한 결과를 형태로 해 돌려준다

    유형 서명의 예関数A : f Int -> String 関数B : f String -> Int関数A : f Int -> Int 関数B : f String -> String
    개념도


    지역 <컨텍스트>



    특정 유형이 동일한 동작과 특성을 가질 때 범위를 권이라고합니다.
    형 인수를 가지는 데이터형은 형태 인수의 권리

    지역 <컨텍스트>의 예リスト: [Int] データ型: type Data = Data Int | String리스트의 값은 리스트권의 값이며, 데이터형의 값은 데이터형권의 값.

    개념도


    컨테이너와 컨텍스트



    데이터형을 가리키고 컨테이너라고 표현하는 경우가 있다.
    컨테이너에 함수를 적용한 경우, 함수로 정의된 데이터형을 인수로 하여 연산을 행한다.
    한편, 문맥에 함수를 적용한 경우, 컨테이너(데이터형)가 가지는 형 변수를 인수로 하여 연산이 행해진다.
    예를 들어, 컨테이너로서의 리스트의 덧셈은 리스트의 연결을 의미하지만, 문맥으로서의 리스트의 덧셈은 리스트의 요소끼리의 합한 값을 돌려준다
    -- コンテナとしてのリストの例
    Prelude> "Hello" ++ ", " ++ "world."
    "Hello, world."
    
    -- コンテキストとしてのリストの例
    Prelude> pure (+) <*> [1..5] <*> [6..10]
    [7,8,9,10,11,8,9,10,11,12,9,10,11,12,13,10,11,12,13,14,11,12,13,14,15]
    

    1. Functor (관수)



    정의: <$>(fmap):: Functor f => (a -> b) -> f a -> f b지역 값 <형 변수>에 함수 적용

    Functor의 예
    1. Functor형 클래스의 인스턴스 a가 인수
    2. (a -> b): a에 함수(->)를 적용한 결과 b가 생성
    3. Functor 형 클래스의 인스턴스에 처리 결과 b를 채우고 반환한다
    예 1) 목록에서 값을 검색하여 처리하고 다른 목록으로 값을 채우고 결과를 반환합니다.
    예 2) Maybe 형으로부터 값을 꺼내 처리를 실시해, Maybe 형에 채우고 결과를 돌려준다

    개념도


    2. Applicative(어플리케이티브)



    정의: <$>(fmap):: Functor f => (a -> b) -> f a -> f b권의 값 <형 변수>를 꺼내 함수를 적용한 후 결과를 권에 담아
    ※Functor로 정의된 메소드

    정의: <*>:: Applicative f => f (a -> b) -> f a -> f b지역 값 <형 변수>에 함수 적용

    정의: pure:: Applicative f => a -> f a형을 Functor의 권에 담아

    Applicative 예제
    1. Applicative 형 클래스의 인스턴스 a가 인수
    2. f(a -> b): 인수와 반환값이 Applicative형의 함수를 적용한 결과 b를 생성
    3. Applicative 형 클래스의 인스턴스에 처리 결과 b를 채우고 반환한다
    Prelude> -- f (a -> b)型の関数を定義
    Prelude> func1 = (^) <$> Just 4
    Prelude> :t func1
    func1 :: (Integral b, Num a) => Maybe (b -> a)
    Prelude> -- f a を渡して処理を実行
    Prelude> func1 <*> Just 2
    Just 16
    Prelude> -- Functorの圏に詰め込む
    Prelude> functor = pure 6
    Prelude> :t functor
    functor :: (Applicative f, Num a) => f a
    Prelude> -- pureと<*>を使った式
    Prelude> pure (^2) <*> Just 4
    Just 16
    Prelude> -- 練習1
    Prelude> (++) <$> Just "Say," <*> Just "Hello"
    Just "Say,Hello"
    Prelude> -- 練習2
    Prelude> pure (+) <*>  [1,2] <*>  [3,4]
    [4,5,5,6]
    Prelude> -- 練習3
    Prelude> pure (+) <*> (pure (+) <*> Just 4 <*> Just 3)  <*> Just 3
    Just 10
    

    3. Monad(모나드)



    정의: <$>(fmap):: Functor f => (a -> b) -> f a -> f b지역 값 <형 변수>에 함수 적용
    ※Functor로 정의된 메소드

    정의: <*>:: Applicative f => f (a -> b) -> f a -> f b권의 값 <형 변수>를 꺼내 권의 함수를 적용한 후 결과를 권에 담는다.
    ※Applicative로 정의된 메소드

    정의: pure:: Applicative f => a -> f a형을 Functor의 권에 담아
    ※Applicative로 정의된 메소드

    정의: >>= :: Monad m => m a -> (a -> m b) -> m b지역에서 값을 검색하고 함수를 적용한 결과를 지역에 넣습니다.

    정의: return :: Monad m => a -> m a값을 권에 담아

    정의: >> :: Monad m => m a -> m b -> m b

    연습


    import qualified Data.Map as Map
    
    type UserName      = String
    type GamerId       = Int
    type PlayerCredits = Int
    
    -- このMapはGameIdに紐づくUserNameを持つ
    userNameDB :: Map.Map GamerId UserName
    userNameDB = Map.fromList [(1, "Asano Aiko")
                             , (2, "Katase Kaito")
                             , (3, "Sato Satoshi")
                             , (4, "Tanabe Kaede")
                             , (5, "Nanase Nanako")]
    
    -- このMapはUserNameに紐づくクレジット情報を持つ
    creditsDB :: Map.Map UserName PlayerCredits
    creditsDB = Map.fromList [("Asano Aiko", 5000)
                            , ("Katase Kaito", 15000)
                            , ("Sato Satoshi", 3000)
                            , ("Tanabe Kaede", 152500)
                            , ("Nanase Nanako", 6500)]
    
    
    -- GamerIdに紐づくデータを取得
    lookupUserName :: GamerId -> Maybe UserName
    lookupUserName gamerId = Map.lookup gamerId userNameDB
    
    -- UserNameに紐づくデータを取得する
    lookupCredits :: UserName -> Maybe PlayerCredits
    lookupCredits userName = Map.lookup userName creditsDB
    
    -- GamerIdからユーザーのクレジット情報を取得する関数
    creditsFromGamerId :: GamerId -> Maybe PlayerCredits
    creditsFromGamerId gamerId =   lookupUserName gamerId >>= lookupCredits
    -------------------------
    -- Maybeを使わないでGamerIdからユーザーのクレジット情報を取得する関数
    lookupCreditsWithoutMonad :: Maybe UserName -> Maybe PlayerCredits
    lookupCreditsWithoutMonad Nothing = Nothing
    lookupCreditsWithoutMonad (Just userName) = lookupCredits userName
    
    creditsFromGamerIdWithoutMonad :: GamerId -> Maybe PlayerCredits
    creditsFromGamerIdWithoutMonad gamerId = lookupCreditsWithoutMonad $ lookupUserName gamerId
    
    *Main> :l monad-sample.hs
    *Main> -- Monadを使わないで実装
    *Main> creditsFromGamerIdWithoutMonad 1
    Just 5000
    *Main> creditsFromGamerIdWithoutMonad 100
    Nothing
    *Main> -- Monadを使って実装
    *Main> creditsFromGamerId 5
    Just 6500
    *Main> creditsFromGamerId 50
    Nothing
    

    감상



    Monad에서 정의되고 있는 함수가 어떤 의미를 갖고, 어떤 움직임을 하는지 개요 레벨이지만 파악할 수 있었다.
    다만 잘 다루기 위해서는 익숙해져야 한다고 느꼈다.

    좋은 웹페이지 즐겨찾기