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