return2について

mclh46さんの記事のreturn2についていろいろと考えました。そして「つまりreturn2は二択returnのことですね」と勝手に結論を付けました。ならば最近見ているControl.Arrowの中にもこういう選択的な機能をサポートする関数があります。

(|||) :: a b d -> a c d -> a (Either b c) d

これを使って何かできないかな?と思い、とりあえず適当に:

return2 :: Bool -> a -> Maybe a
return2 x = (genSum x >>> id ||| id)
  where genSum t a | t == True = Left (Just a)
                   | otherwise = Right Nothing

うん、動きますが、Maybe限定の特殊ケースなのでよろしくない。例えばListを返したい場合はJust a → [a]とNothing → のようにいちいち修正しなければならない。ここでMaybeとの共通点について考えれば、そう、答えはMonadPlusクラスです。

--Monads that also support choice and failure. 
class Monad m => MonadPlus m where
  ...

つまり、こうすればOKですね:

return2 :: (MonadPlus m) => Bool -> a -> m a
return2 x = (genSum x >>> id ||| id)
  where genSum t a | t == True = Left (return a)
                   | otherwise = Right mzero

テスト:

import Control.Arrow
import Control.Monad

stockA = 3
stockB = 0
priceA = 100
priceB = 200

--一番効率な実装はこれです、ながとさんありがとう
--return2 :: (MonadPlus m) => Bool -> a -> m a
--return2 True v = return v
--return2 _    _ = mzero

return2 :: (MonadPlus m) => Bool -> a -> m a
return2 x = (genSum x >>> id ||| id)
  where genSum t a | t == True = Left (return a)
                   | otherwise = Right mzero

main :: IO()
main = print (getA::Maybe Integer) >> print (getB::Maybe Integer) >>
       print (getA'::[Integer])    >> print (getB'::[Integer])
  where getA = return2 (stockA > 0) priceA
        getB = return2 (stockB > 0) priceB
        getA' = return2 (stockA > 0) priceA
        getB' = return2 (stockB > 0) priceB

Output:

Just 100
Nothing
[100]
[]