How to mix Haskell monadic and clean piping filters?

In the previous question, I tried to ask how to mix pure and monadic functions, combining them together, but since I might have mis-formulated my question and my example was too simplistic, I think the discussion went in the wrong direction, so I think I’ll try again.

Here is an example function that mixes clean and monadic filters. In this example, there are several clean filters arranged between monadic filters to try to reduce the amount of work.

findFiles target = 
  getDirectoryContents target                    >>=
  return . filter (not . (=~ "[0-9]{8}\\.txt$")) >>=
  return . filter (=~ "\\.txt$")                 >>=
  filterM doesFileExist                          >>=
  mapM canonicalizePath

The advantage of writing this method when pure functions are mixed in use returnis that there is a visual flow of data from top to bottom. No need for temporary variables fmap, <$>or the like.

Ideally, I can get rid of returnto make it cleaner. I had an idea to use some operator:

(|>=) :: Monad m => a -> (a -> m b) -> m b
a |>= b = (return a) >>= b

But I don’t know how to write this function to avoid priority issues. Does it already exist? It looks like <$>, but in a "different direction." If not, how do I get this statement to work?

More generally, is there a good way to write code in this pipeline method, or do I need to set for fmapand temporary variables as described in my previous question?

+4
4

. :

infixl 1 |>=
(|>=) = flip fmap

findFiles target = 
  getDirectoryContents target           |>=
  filter (not . (=~ "[0-9]{8}\\.txt$")) |>=
  filter (=~ "\\.txt$")                 >>=
  filterM doesFileExist                 >>=
  mapM canonicalizePath
+7

DiegoNolan, do -notation, (x <- ...) let. .

, , . , (. ) :

import Control.Arrow

findFiles = runKleisli $
    Kleisli getDirectoryContents >>>
    arr (filter (not . (=~ "[0-9]{8}\\.txt$"))) >>>
    arr (filter (=~ "\\.txt$")) >>>
    Kleisli (filterM doesFileExist) >>>
    Kleisli (mapM canonicalizePath)

, , , , , , . De gustibus non est debandum, .

+3

(<$>), fmap, . . , liftM

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

(<$>) :: Functor f => (a -> b) -> f a -> f b

( ghc).

findFiles target = 
  ((filter (not . (=~ "[0-9]{8}\\.txt$")) .
  filter (=~ "\\.txt$")                 )        <$>
  getDirectoryContents target)                   >>=
  filterM doesFileExist                          >>=
  mapM canonicalizePath

, , let.

+1

,

Monad -> Monad
Monad -> Pure
Pure  -> Monad
Pure  -> Pure

Monad -> Monad (>>=), , , |>= Pure -> Monad, Monad -> Pure . , - .

Monad -> Monad    >>=    m a -> (a -> m b) -> m b
Monad -> Pure     >|=    m a -> (a ->   b) -> m b
Pure  -> Monad    |>=      a -> (a -> m b) -> m b
Pure  -> Pure     ||=    (a -> b) -> (b -> c) -> (a -> c)

, > "monad" | "", =, "to function". , :

import Data.Char (toUpper)
import Control.Monad (liftM)

infixl 1 |>=
(|>=) :: Monad m => a -> (a -> m b) -> m b
a |>= b = b a

infixl 1 >|=
(>|=) :: Monad m => m a -> (a -> b) -> m b
a >|= b = liftM b a

infixr 9 ||=
(||=) :: (a -> b) -> (b -> c) -> a -> c
a ||= b = b . a

test :: IO ()
test =
    getLine             >|=
    filter (/= 't')     ||=
    map toUpper         >>=
    putStrLn

> test
testing
ESING
>

test :: IO ()
test =
    getLine         >|=
    filter (/= 't') >|=
    map toUpper     >>=
    putStrLn

But the added combination ||>would allow you to actually compose those functions that have different implementations under the hood than feed them through monadic actions.

However, I still urge you to use the idiomatic way to do this using fmap, make notation and temporary variables. This will be much clearer to anyone looking at the code, and this will include you in 2 months.

+1
source

All Articles