0. まとめ
liftA2 関数および liftA3 関数以外の持ち上げ関数は計算コストが高い可能性があるため、liftA2 関数および liftA3 関数や演算子 <$> および演算子 <*> を使用します:
liftA-
liftM,liftM2,liftM3,liftM4,liftM5 ap
Applicative クラスと Monad クラスで同じ機能が提供されている場合、Monad クラスでなく Applicative クラスを使用します:
- 基本
return = pure(>>) = (*>)mzero = emptymplus = (<|>)
- ループ
-
mapM_ = traverse_,forM_ = for_ sequence_ = sequenceA_
-
- リストのような関数
-
mapM = traverse、forM = for sequence = sequenceAmsum = asum
-
可能であれば型クラス制約 MonadPlus m でなく以下のものを使用します:
Alternarive mMonad mAlternarive m, Monad m
ビット演算に関して、演算子が提供されている場合は関数の中置記法でなく演算子を使用します:
(.^.) = xor-
(.<<.) = shiftL,(!<<.) = unsafeShiftL -
(.>>.) = shiftR,(!>>.) = unsafeShiftR
1. 持ち上げ関数
liftA2 関数は元はメソッドでありませんでしたが、現在は Applicative クラスのメソッドになっています。
liftA2 関数や演算子 <*> はメソッドのため、具体的な型ごとに最適な計算をするはずです。
演算子 <$> は (<$>) = fmap と定義されていて、fmap 関数は Functor クラスのメソッドのため、最適な計算をするはずです。
liftA3 関数は liftA2 関数と演算子 <*> を用いて定義されているため、(他にその型専用の関数がなければ) 最適な計算を期待できます。
liftA2 関数および liftA3 関数以外の持ち上げ関数は最適な計算をするとは限らないため、使用しない方が良いです。
liftA f x
liftM f x
f `liftA` x
f `liftM` x
liftM2 f x y
liftM3 f x y z
pure f `ap` x1 `ap` x2 `ap` x3 `ap` x4 `ap` x5 `ap` x6
f <$> x
liftA2 f x y
liftA3 f x y z
liftA2 f x y <*> z
liftA2 f x1 x2 <*> x3 <*> x4 <*> x5 <*> x6
liftA3 f x1 x2 x3 <*> x4 <*> x5 <*> x6
参考「[Haskell] モナド 演算子 まとめ - Qiita」
参考「[Haskell] f <$> x <*> y よりも liftA2 f x y の方が計算コストを抑えられることがある - Qiita」
参考「(<$>) - Data.Functor」
参考「(<*>) - Control.Applicative」
参考「liftA2 - Control.Applicative」
参考「liftA3 - Control.Applicative」
2. Applicative クラスと Monad クラス
Monad クラスは元々 Applicative クラスのサブクラスでありませんでしたが、現在はサブクラスになっています。
そのため、Monad クラスと Applicative クラスで同じ機能が提供されていることがあります。
もし初めから Monad クラスが Applicative クラスのサブクラスであれば Monad クラスでは提供されなかったはずの機能のため、Applicative クラスを使用します。
2.1. 基本
return x
x >> y
mzero
mplus x y
x `mplus` y
pure x
x *> y
empty
x <|> y
※ちなみに mempty 関数および mconcat 関数は MonadPlus クラスのメソッドでなく Monoid クラスのメソッドであり、ここでは関係ありません。
参考「[Haskell] モナド 演算子 まとめ - Qiita」
参考「[Haskell] モナドの計算を中断する - Qiita」
参考「pure - Control.Applicative」
参考「(*>) - Control.Applicative」
参考「empty - Control.Applicative」
参考「(<|>) - Control.Applicative」
2.2. ループ
mapM_ f xs
forM_ xs f
sequence_ xs
traverse_ f xs
for_ xs f
sequenceA_ xs
参考「[Haskell] IO () 型の for ループ - Qiita」
参考「traverse_ - Data.Foldable」
参考「for_ - Data.Foldable」
参考「sequenceA_ - Data.Foldable」
2.3. リストのような関数
mapM f xs
forM xs f
sequence xs
msum xs
traverse f xs
for xs f
sequenceA xs
asum xs
参考「traverse - Data.Traversable」
参考「for - Data.Traversable」
参考「sequenceA - Data.Traversable」
参考「asum - Data.Foldable」
2.4. MonadPlus クラス
型クラス制約 MonadPlus m は可能であれば Alternative m と Monad m に分けます。
例として、mfilter 関数の型クラス制約 MonadPlus m を書き換えると以下のようになります。
※説明のためソースコードを一部改変。
mfilter :: MonadPlus m => (a -> Bool) -> m a -> m a
mfilter p mx = do
x <- mx
if p x
then return x
else mzero
mfilter' :: (Alternative m, Monad m) => (a -> Bool) -> m a -> m a
mfilter' p mx = do
x <- mx
if p x
then pure x
else empty
※ちなみに return x や pure x を mx としてしまうと型によっては動作が変わるため、mx には書き換えられません。
3. ビット演算
一部のビット演算に関して、ビット演算子が追加されています。
x `xor` y
x `shiftL` i
x `unsafeShiftL` i
x `shiftR` i
x `unsafeShiftR` i
x .^. y
x .<<. i
x !<<. i
x .>>. i
x !>>. i
参考「(.^.) - Data.Bits」
参考「(.<<.) - Data.Bits」
参考「(!<<.) - Data.Bits」
参考「(.>>.) - Data.Bits」
参考「(!>>.) - Data.Bits」