8

Haskell のControl.Arrowドキュメントでは、Kleisli の矢印とモナドの関係について説明していますが、これをどのように使用するかは私には明らかではありません。私は IO モナドを除いて矢印に合うと思う関数を持っているので、Kleisli の矢印が役立つと思います。

ディレクトリの元のファイル名と変更されたファイル名のペアを返す次の関数を使用します。

import System.Directory
import System.FilePath

datedFiles target = do
    fns <- getDirectoryContents target
    tms <- mapM (fmap show . getModificationTime) fns
    return $ 
        zip fns $ 
        zipWith replaceBaseName fns $ 
        zipWith (++) (map takeBaseName fns) tms

それを描くとしたら、次のようになります。

ここに画像の説明を入力

Kleisli の矢を使用すると効果があると思いますが、方法がわかりません。誰でもガイダンスを提供できますか?

4

4 に答える 4

6

datedFiles図が示すように、情報は「固定パイプライン」で流れるため、矢印を使用して実装できます。

リストでmaporを使用しない実装の例を次に示します。zip

import System.Directory
import System.FilePath
import Control.Monad.List
import Control.Arrow

datedFiles :: FilePath -> IO [(FilePath,FilePath)]
datedFiles = fmap runListT . runKleisli $
   (Kleisli $ ListT . getDirectoryContents) 
   >>>
   returnA &&& ((Kleisli $ liftIO . getModificationTime) >>^ show)
   >>^
   fst &&& (\(path,time) -> replaceBaseName path $ takeBaseName path ++ time)

おそらく、これは最も直感的な実装ではありません。

Kleisli アローのモナドは ですListT IOが、唯一の非決定性は によって引き起こされgetDirectoryContentsます。

最後の行は純関数であることに注意してください。最後の(&&&)行は、関数の Arrow インスタンスを使用しています。

編集:パッケージのWrapped型クラスをlens使用して、newtype ラッパーをもう少し簡潔に追加/削除できます。前の例に適用すると、次のようになります。

import Control.Lens

datedFiles :: FilePath -> IO [(FilePath,FilePath)]
datedFiles = fmap runListT . runKleisli $
   ListT . getDirectoryContents ^. wrapped 
   >>>
   returnA &&& (liftIO . getModificationTime ^. wrapped >>^ show)
   >>^
   fst &&& (\(path,time) -> replaceBaseName path $ takeBaseName path ++ time)
于 2013-11-23T19:02:19.873 に答える
1

Monad の main 関数を思い出してみましょう:

(>>=) :: (a -> m b) -> m a  -> m b

そして今見てみましょうKleisli

newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }

ここで、newtypeKleisliのラッパーと- アンラッパーです。runKleisli

共通点は何ですか?a -> m b

そして、インスタンス宣言を見てみましょう:

instance Monad m => Arrow (Kleisli m) where ...

なるほど、Monad一部を作る方法Arrow

于 2013-11-24T17:46:14.190 に答える