3

Haskellで幅優先でディレクトリを再帰的にリストできる関数を書きたいです。ご覧のとおり、a(a-> IO b)をIO(a-> b)に変換できる関数が必要です。見た目は単純ですが、私にはできません。そして、その方法やそれが可能かどうかを知りたいです。

dirElem :: FilePath -> IO [FilePath]
dirElem dirPath = do
  getDirectoryContents'' <- theConvert getDirectoryContents'
  return $ takeWhile (not.null) $ iterate (concatMap getDirectoryContents'') [dirPath] where
    getDirectoryContents' dirPath = do
      isDir <- do doesDirectoryExist dirPath
      if isDir then dirContent else return [] where
        dirContent = do
          contents <- getDirectoryContents dirPath
          return.(map (dirElem</>)).tail.tail contents
    theConvert :: (a -> IO b) -> IO (a -> b)
    theConvert = ??????????
4

4 に答える 4

6

一般に純粋な方法でそれを行うことはできませんが、すべての引数値を列挙できれば、すべてのIOを事前に実行して、純粋関数を返すことができます。何かのようなもの

cacheForArgs :: [a] -> (a -> IO b) -> IO (a -> b)
cacheForArgs as f = do
    bs <- mapM f as
    let abs = zip as bs
    return $ \ a -> fromMaybe (error "argument not cached") $ lookup a abs

cacheBounded :: (Enum a, Bounded a) => (a -> IO b) -> IO (a -> b)
cacheBounded = cacheForArgs [minBound .. maxBound]

しかし、この関数は、ユースケースではあまり役に立ちません。

于 2013-01-21T08:30:48.850 に答える
6

これはできません。その理由は、関数がその型の引数を使用して、実行されるアクションaを判別できるためです。IO検討

action :: Bool -> IO String
action True  = putStrLn "Enter something:" >> getLine
action False = exitFailure

さて、それをなんとかしてIO (Bool -> String)このアクションに変換して評価するとしたら、どうなるでしょうか?解決策はありません。引数がまだわからないため、文字列を読み取るか終了するかを決定できませBoolん(また、結果の関数が引数で呼び出されない場合は、引数がわからない可能性があります)。

ジョンの答えは悪い考えです。それは単にIOアクションを純粋な計算に逃がすだけであり、それはあなたの人生を悲惨にし、Haskellの参照透過性を失うでしょう!例:実行中:

main = unsafe action >> return ()

IOアクションが呼び出されても、何も実行されません。さらに、少し変更すると、次のようになります。

main = do
   f <- unsafe action
   putStrLn "The action has been called, calling its pure output function."
   putStrLn $ "The result is: " ++ f True

action入力を要求するは、呼び出し内の純粋な計算で実行されることがわかりますf。アクションがいつ実行されるか(あるとしても)は保証されません!

編集:他の人が指摘したように、それはだけに固有のものではありませんIO。たとえば、モナドがの場合、Maybeを実装することはできません(a -> Maybe b) -> Maybe (a -> b)。またはEither、の場合、を実装できませんでした(a -> Either c b) -> Either c (a -> b)。重要なのは、効果を修正する必要がある一方で、にa -> m b応じてさまざまな効果を選択できるということです。am (a -> b)

于 2013-01-21T21:16:25.473 に答える
4

このような関数を安全に作成することはできません。私たちが持っていると言うf :: a -> IO bg = theConvert f :: IO (a -> b)。これらは2つの非常に異なる関数fであり、型の引数を取り、結果を含むアクションをa返す関数です。ここで、io-actionは指定された引数に依存する場合があります。一方、結果として関数を持つアクションである場合、io-actionは引数に依存できません。この問題を説明するために、見てみましょうIObgIOa->b

theConvert putStr :: IO (String -> ())

実行時に何をすべきか、引数がないため、特定の引数を出力することはできません。String -> ()したがって、putStrとは異なり、アクションを1つだけ実行してから、オプションが1つしかないタイプの関数を返すことができます(またはconst ()を使用しないと仮定します)。errorundefined


逆に、逆の方法も可能ですが、実際にはそうではないのに、結果のアクションは引数に依存するという概念が追加されます。それは次のように書くことができます

theOtherConvert :: IO (a -> b) -> (a -> IO b)
theOtherConvert m x = m >>= \f -> return $ f x

それはどんなモナドでも、または適用可能な形で機能しますがtheOtherConvert m x = m <*> pure x

于 2013-01-21T10:47:32.737 に答える
3

PetrPudlákの答えは素晴らしいですが、から抽象化し、型クラスと型クラスIOの観点から見ることで一般化できると思います。ApplicativeMonad

Applicativeおよびからの「結合」操作のタイプを検討してくださいMonad

(<*>) :: Applicative m => m (a -> b) -> m a -> m b
(>>=) :: Monad m => m a -> (a -> m b) -> m b

つまり、タイプa -> IO bは「モナディック」でありIO (a -> b)、「適用可能」であると言えます。つまり、のように見えるタイプを作成するには、モナディック操作が必要ですがa -> IO bIO (a -> b)

との間の「力」の違いについてのよく知られた直感的なステートメントがMonadありApplicativeます:

  • 適用可能な計算には、固定された静的構造があります。どのアクションが実行されるか、どのような順序で実行されるか、および結果がどのように組み合わされるかは、事前にわかっています。
  • モナディック計算には、そのような固定された静的構造はありません。モナディック計算では、サブアクションの1つからの結果値を調べて、実行時に異なる構造から選択できます。

Petrの答えは、この点を具体的に示しています。私は彼の定義を繰り返しますaction

action :: Bool -> IO String
action True  = putStrLn "Enter something:" >> getLine
action False = exitFailure

あるとしますfoo :: IO Bool。次に、のパラメータをの結果foo >>= actionにバインドするように書き込むと、結果の計算は、私の2番目の箇条書きで説明されていることと同じです。実行の結果を調べ、その値に基づいて代替アクションから選択します。これはまさにあなたがそうすることを可能にすることの1つです。同時にどのブランチを選択するかを事前に決定しない限り、Petrに参加することはできません。actionfoofooMonadApplicativeactionIO (Bool -> String)

同様の意見がオーガストの反応にも当てはまります。値のリストを事前に指定する必要があるため、事前に取得するブランチを選択し、すべてを取得して、結果から選択できるようになります。

于 2013-01-21T23:56:14.850 に答える