Apply function to file if it exists

I have a function that applies a function to a file if it exists:

import System.Directory
import Data.Maybe

applyToFile :: (FilePath -> IO a) -> FilePath -> IO (Maybe a)
applyToFile f p = doesFileExist p >>= apply
  where 
    apply True  = f p >>= (pure . Just)
    apply False = pure Nothing

Usage example:

applyToFile readFile "/tmp/foo"
applyToFile (\p -> writeFile p "bar") "/tmp/foo"

Abstraction level can be added:

import System.Directory
import Data.Maybe

applyToFileIf :: (FilePath -> IO Bool) -> (FilePath -> IO a) ->  FilePath -> IO (Maybe a)
applyToFileIf f g p = f p >>= apply
  where 
    apply True  = g p >>= (pure . Just)
    apply False = pure Nothing

applyToFile :: (FilePath -> IO a) -> FilePath -> IO (Maybe a)
applyToFile f p = applyToFileIf doesFileExist f p 

This permits the use of:

applyToFileIf (\p -> doesFileExist p >>= (pure . not)) (\p -> writeFile p "baz") "/tmp/baz"

I have a feeling that I just scratched the surface, and there is a more general hiding pattern.
Are there better abstractions or more idiomatic ways to do this?

+4
source share
1 answer

applyToFileIf a more general type and a more general name can be specified

applyToIf :: Monad m => (a -> m Bool) -> (a -> m b) -> a -> m (Maybe b)
applyToIf f g p = f p >>= apply
  where 
    apply True  = g p >>= (return . Just)
    apply False = return Nothing

In the type applyToIfwe see the composition of two Monads

                                           Maybe is a monad ---v 
applyToIf :: Monad m => (a -> m Bool) -> (a -> m b) -> a -> m (Maybe b)
                   ^------------- m is a monad -------------^

, , - , , . MaybeT m (Maybe a)

newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

MonadPlus , m.

instance (Monad m) => MonadPlus (MaybeT m) where ...

applyToIf, , MonadPlus

import Control.Monad

applyToIf :: MonadPlus m => (a -> m Bool) -> (a -> m b) -> a -> m b
applyToIf f g p = f p >>= apply
  where 
    apply True  = g p
    apply False = mzero

guard Control.Monad .

guardBy :: MonadPlus m => (a -> m Bool) -> (a -> m b) -> a -> m b
guardBy f g p = f p >>= apply
  where 
    apply b = guard b >> g p

g , guardBy. guardBy f g p guardBy f return p >>= g. .

guardBy :: MonadPlus m => (a -> m Bool) -> a -> m a
guardBy f p = f p >>= \b -> guard b >> return p

MaybeT . applyToIf .

import Control.Monad.Trans.Class
import Control.Monad.Trans.Maybe

applyToIf ::  Monad m => (a -> m Bool) -> (a -> m b) -> a -> m (Maybe b)
applyToIf f g = runMaybeT . (>>= lift . g) . guardBy (lift . f)

,

import Control.Monad.IO.Class

(MonadPlus m, MonadIO m) =>
    ...
    guardBy (liftIO . doesFileExist) filename >>= liftIO . readFile
+6

Source: https://habr.com/ru/post/1608147/


All Articles