LoginSignup
5
5

[Haskell] モナド 演算子 まとめ

Last updated at Posted at 2023-10-14

本記事ではモナドを操作する演算子について書きます。

参考「Data.Functor
参考「Control.Applicative
参考「Control.Monad

0. まとめ

用途別:

  • 値に関数を適用する
    • <$>, <&>
    • <*>, <**>
    • =<<, >>=
    • <$!>
  • 関数同士を合成する
    • <=<, >=>
  • 値を破棄して他の値に置き換える
    • <$, $>
    • <*, *>
    • >>
  • 値同士を結合する
    • <|>

型クラス制約別:

  • Functor
    • <$>, <&>
    • <$, $>
  • Applicative
    • <*>, <**>
    • <*, *>
  • Monad
    • =<<, >>=
    • <=<, >=>
    • >>
    • <$!>
  • Alternative
    • <|>

>> は歴史的な理由により残されていますが、(>>) = (*>) です。
<$!><$> の正格版です。

ここでは説明のため各演算子の型クラス制約を略しますが、型変数 f の型は Functor, Applicative, Alternative クラスのインスタンス、型変数 m の型は Monad クラスのインスタンスです。

($)    ::   (a ->   b) ->   a ->   b -- (比較用)
(<$>)  ::   (a ->   b) -> f a -> f b -- Functor
(<*>)  :: f (a ->   b) -> f a -> f b -- Applicative
(=<<)  ::   (a -> m b) -> m a -> m b -- Monad

(&)    ::   a ->   (a ->   b) ->   b -- (比較用)
(<&>)  :: f a ->   (a ->   b) -> f b -- Functor
(<**>) :: f a -> f (a ->   b) -> f b -- Applicative
(>>=)  :: m a ->   (a -> m b) -> m b -- Monad
(.)   :: (b ->   c) -> (a ->   b) -> a ->   c -- (比較用)
(<=<) :: (b -> m c) -> (a -> m b) -> a -> m c -- Monad

(>=>) :: (a -> m b) -> (b -> m c) -> a -> m c -- Monad
const ::   a ->   b ->   a -- (比較用)
(<$)  ::   a -> f b -> f a -- Functor
(<*)  :: f a -> f b -> f a -- Applicative

($>)  :: f a ->   b -> f b -- Functor
(*>)  :: f a -> f b -> f b -- Applicative
(<>)  ::   a ->   a ->   a -- (比較用)
(<|>) :: f a -> f a -> f a -- Alternative

1. 値に関数を適用する

1.1. 一般: $, &

a 型の値に a -> b 型の関数を適用して b 型の値を得ます。

import Data.Function ((&))

f :: Int -> Int
f = (* 42)

g :: Int -> Int
g = subtract 1729

main :: IO ()
main = do

    let x = 23 :: Int

    -- 
    print (f x :: Int)

    print (g . f $ x :: Int)

    -- 
    print (x & f :: Int)

    print (x & f & g :: Int)

関数の引数が複数ある場合は以下のようになります。

import Data.Function ((&))

f :: Int -> Int -> Int -> Int
f x y z = x * y - z

main :: IO ()
main = do

    let x = 23   :: Int
    let y = 42   :: Int
    let z = 1729 :: Int

    print (f x y z :: Int)

    print (z & (y & (x & f)) :: Int)

1.2. Functor: <$>, <&>

インスタンス Functor f に関して、f a 型の値に a -> b 型の関数を適用して f b 型の値を得ます。

import Data.Functor ((<&>))

f :: Int -> Int
f = (* 42)

g :: Int -> Int
g = subtract 1729

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int

    -- 
    print (f <$> x :: Maybe Int)

    print (g . f <$> x :: Maybe Int)

    -- 
    print (x <&> f :: Maybe Int)

    print (x <&> f <&> g :: Maybe Int)

関数の引数が複数ある場合は以下のようになります。

import Control.Applicative ((<**>), liftA2, liftA3)
import Data.Functor ((<&>))

f :: Int -> Int -> Int -> Int
f x y z = x * y - z

main :: IO ()
main = do

    let x = Just 23   :: Maybe Int
    let y = Just 42   :: Maybe Int
    let z = Just 1729 :: Maybe Int

    -- 
    print (f <$> x <*> y <*> z :: Maybe Int)

    print (liftA3 f x y z     :: Maybe Int)
    print (liftA2 f x y <*> z :: Maybe Int)

    -- 
    print (z <**> (y <**> (x <&> f)) :: Maybe Int)

liftA2 関数および liftA3 関数については別記事参照。

参考「[Haskell] f <$> x <*> y よりも liftA2 f x y の方が計算コストを抑えられることがある - Qiita

1.3. Applicative: <*>, <**>

インスタンス Applicative f に関して、f a 型の値に f (a -> b) 型の関数を適用して f b 型の値を得ます。

import Control.Applicative ((<**>))

f :: Maybe (Int -> Int)
f = Just (* 42)

g :: Maybe (Int -> Int)
g = Just $ subtract 1729

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int

    -- 
    print (f <*> x :: Maybe Int)

    print (g <*> (f <*> x) :: Maybe Int)

    -- 
    print (x <**> f :: Maybe Int)

    print (x <**> f <**> g :: Maybe Int)

関数の引数が複数ある場合は以下のようになります。

import Control.Applicative ((<**>))

f :: Maybe (Int -> Int -> Int -> Int)
f = Just $ \x y z -> x * y - z

main :: IO ()
main = do

    let x = Just 23   :: Maybe Int
    let y = Just 42   :: Maybe Int
    let z = Just 1729 :: Maybe Int

    print (f <*> x <*> y <*> z :: Maybe Int)

    print (z <**> (y <**> (x <**> f)) :: Maybe Int)

1.4. Monad: =<<, >>=

インスタンス Monad m に関して、m a 型の値に a -> m b 型の関数を適用して m b 型の値を得ます。

import Control.Monad ((<=<), (>=>))

f :: Int -> Maybe Int
f = Just . (* 42)

g :: Int -> Maybe Int
g = Just . subtract 1729

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int

    -- 
    print (f =<< x :: Maybe Int)

    print (g =<< f =<< x :: Maybe Int)

    print ((g <=< f) =<< x :: Maybe Int)

    -- 
    print (x >>= f :: Maybe Int)

    print (x >>= f >>= g :: Maybe Int)

    print (x >>= (f >=> g) :: Maybe Int)

上記のコードを do 記法の <- を用いて書くと以下のようになります。

f :: Int -> Maybe Int
f = Just . (* 42)

g :: Int -> Maybe Int
g = Just . subtract 1729

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int

    print $ do
        y :: Int <- x :: Maybe Int
        f y :: Maybe Int

    print $ do
        y :: Int <- x   :: Maybe Int
        z :: Int <- f y :: Maybe Int
        g z :: Maybe Int

2. 関数同士を合成する

2.1. 一般: .

a -> b 型の関数と b -> c 型の関数を合成して a -> c 型の関数を得ます。

f :: Int -> Int
f = (* 42)

g :: Int -> Int
g = subtract 1729

main :: IO ()
main = do

    let x = 23 :: Int

    print $ (g . f :: Int -> Int) x

2.2. Monad: <=<, >=>

インスタンス Monad m に関して、a -> m b 型の関数と b -> m c 型の関数を合成して a -> m c 型の関数を得ます。

import Control.Monad ((<=<), (>=>))

f :: Int -> Maybe Int
f = Just . (* 42)

g :: Int -> Maybe Int
g = Just . subtract 1729

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int

    print $ (g <=< f :: Int -> Maybe Int) =<< x

    print $ x >>= (f >=> g :: Int -> Maybe Int)

上記のコードを do 記法の <- を用いて書くと以下のようになります。

f :: Int -> Maybe Int
f = Just . (* 42)

g :: Int -> Maybe Int
g = Just . subtract 1729

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int

    print
        $ (\y -> do
            z :: Int <- f y :: Maybe Int
            g z :: Maybe Int
        )
        =<< x

    print $ x >>= \y -> do
        z :: Int <- f y :: Maybe Int
        g z :: Maybe Int

3. 値を破棄して他の値に置き換える

3.1. 一般: const

a 型の値と b 型の値から、b 型の値を破棄して a 型の値を得ます。

f :: Int -> () -> Int
f = const

main :: IO ()
main = do

    let x = 23 :: Int
    let y = () :: ()

    print (x `f` y :: Int)

3.2. Functor: <$, $>

インスタンス Functor f に関して、a 型の値と f b 型の値から、b 型の値を破棄して f a 型の値を得ます。

import Data.Functor (($>))

main :: IO ()
main = do

    let x = 23      :: Int
    let y = Just () :: Maybe ()

    print (x <$ y :: Maybe Int)

    print (y $> x :: Maybe Int)

3.3. Applicative: <*, *>

インスタンス Applicative f に関して、f a 型の値と f b 型の値から、b 型の値を破棄して f a 型の値を得ます。

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int
    let y = Just () :: Maybe ()

    print (x <* y :: Maybe Int)

    print (y *> x :: Maybe Int)

インスタンス Monad m の場合、上記のコードを do 記法を用いて書くと以下のようになります。

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int
    let y = Just () :: Maybe ()

    print $ do
        y :: Maybe ()
        x :: Maybe Int

3.4. const<* の違い

インスタンス Applicative f に関して、f a 型の値と f b 型の値から f a 型の値を得るとき、f b 型の値を破棄するか b 型の値を破棄するかが異なります。

f :: Maybe Int -> Maybe () -> Maybe Int
f = const

main :: IO ()
main = do

    let x = Just 23 :: Maybe Int
    let y = Nothing :: Maybe ()

    print (x `f` y :: Maybe Int)

    print (x <* y :: Maybe Int)
実行結果
Just 23
Nothing

4. 値同士を結合する

4.1. Semigroup: <>

インスタンス Semigroup a に関して、a 型の値 2 つを結合します。

main :: IO ()
main = do

    print $ ([23] :: [Int]) <> ([42] :: [Int])

    print $ (Right 23 :: Either String Int) <> (Right 42 :: Either String Int)

    print $ (() :: ()) <> (() :: ())

    print =<< (pure () :: IO ()) <> (pure () :: IO ())

4.2. Alternative: <|>

全ての Monad クラスのインスタンスで使えるわけではありませんが、主に MonadPlus クラスのインスタンスに関して <|> を使うことができます。

(※ Monad でも MonadPlus でもない Alternative クラスのインスタンスである ZipList 型のような例外も存在します。)

インスタンス Alternative f に関して、f a 型の値 2 つを結合します。

import Control.Applicative ((<|>))

main :: IO ()
main = do

    print $ ([23] :: [Int]) <|> ([42] :: [Int])

    print $ (Just 23 :: Maybe Int) <|> (Just 42 :: Maybe Int)

    print =<< (pure () :: IO ()) <|> (pure () :: IO ())

    print =<< (pure 23 :: IO Int) <|> (pure 42 :: IO Int)

4.3. <><|> の違い

<><|> は動作は非常に似ていますが、使用できる型が異なります。

List 型に関しては Semigroup クラスと Alternative クラスの両方のインスタンスのため、実際に (<>) = (++) かつ (<|>) = (++) という全く同じ動作をします。

参考「Data.List

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5