本記事ではモナドを操作する演算子について書きます。
参考「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」