LoginSignup
2
0

イマドキな Haskell で書かないもの: return, >>, forM_, mzero, mplus, `shiftL`, その他

Last updated at Posted at 2023-12-03

0. まとめ

liftA2 関数および liftA3 関数以外の持ち上げ関数は計算コストが高い可能性があるため、liftA2 関数および liftA3 関数や演算子 <$> および演算子 <*> を使用します:

  • liftA
  • liftM, liftM2, liftM3, liftM4, liftM5
  • ap

Applicative クラスと Monad クラスで同じ機能が提供されている場合、Monad クラスでなく Applicative クラスを使用します:

  • 基本
    • return = pure
    • (>>) = (*>)
    • mzero = empty
    • mplus = (<|>)
  • ループ
    • mapM_ = traverse_, forM_ = for_
    • sequence_ = sequenceA_
  • リストのような関数
    • mapM = traverseforM = for
    • sequence = sequenceA
    • msum = asum

可能であれば型クラス制約 MonadPlus m でなく以下のものを使用します:

  • Alternarive m
  • Monad m
  • Alternarive 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 mMonad 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 xpure xmx としてしまうと型によっては動作が変わるため、mx には書き換えられません。

参考「mfilter - Control.Monad

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

2
0
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
2
0