I have a function that I want to use a Maybe val with. Usually I would do func <$> val. But now suppose that func uses the IO monad. func <$> val will return a Maybe (IO ()). So instead I had to define a new operator:
(<$$>) :: Monad m => (a -> m b) -> Maybe a -> m ()
(<$$>) func (Just val) = func val >> return ()
(<$$>) func Nothing = return ()
So now I can write func <$$> val, but is there a better way to do it?
mapM_ from Data.Foldable is probably the best match:
Prelude Data.Foldable> :set -XScopedTypeVariables
Prelude Data.Foldable> :t \f (a :: Maybe a) -> Data.Foldable.mapM_ f a
\f (a :: Maybe a) -> Data.Foldable.mapM_ f a
:: Monad m => (a -> m b) -> Maybe a -> m ()
If you'd like a more specialised type there's also maybe:
Prelude> :t \f -> maybe (return ()) (f $)
\f -> maybe (return ()) (f $)
:: Monad m => (a -> m ()) -> Maybe a -> m ()
Your <$$> is traverse_ from Data.Foldable.
Is a one-liner always better? Here's how purity of undefined can be useful:
(<$$>) g x = maybe (return undefined) g x >> return ()
Example:
Prelude> print <$$> (Just 1)
1
Prelude> print <$$> Nothing
Prelude>
If you have a lot of this in your code, it might be worth employing the MaybeT transformer:
(\func val -> liftIO . func =<< MaybeT (return val) )
:: (a -> IO b) -> Maybe b -> MaybeT IO b
That doesn't immediately bring you any further than plain IO (Maybe ()), but it composes nicely.
Related
I have a function that apply 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"
A level of abstraction can be added with:
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
That allow usages like:
applyToFileIf (\p -> doesFileExist p >>= (pure . not)) (\p -> writeFile p "baz") "/tmp/baz"
I have the feeling that I just scratched the surface and there is a more generic pattern hiding.
Are there better abstractions or more idiomatic ways to do this?
applyToFileIf can be given a more generic type and a more generic name
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 of applyToIf we 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 -------------^
When we see the composition of two monads, we can expect that it could be replaced with a monad transformer stack and some class describing what that monad transformer adds. The MaybeT transformer replaces m (Maybe a)
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
And adds MonadPlus to what an m can do.
instance (Monad m) => MonadPlus (MaybeT m) where ...
We'll change the type of applyToIf to not have a composition of two monads and instead have a MonadPlus constraint on a single monad
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
This could be rewritten in terms of guard from Control.Monad and given a more generic name.
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
The second g argument adds nothing to what guardBy can do. guardBy f g p can be replaced by guardBy f return p >>= g. We will drop the second argument.
guardBy :: MonadPlus m => (a -> m Bool) -> a -> m a
guardBy f p = f p >>= \b -> guard b >> return p
The MaybeT transformer adds possible failure to any computation. We can use it to recreate applyToIf or use it more generally to handle failure through complete programs.
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)
If you instead rework the program to use monad style classes, it might include a snippet like
import Control.Monad.IO.Class
(MonadPlus m, MonadIO m) =>
...
guardBy (liftIO . doesFileExist) filename >>= liftIO . readFile
I have a function
parseArgs :: [String] -> StdGen -> IO ()
which selects the function to run. The main looks like
main = parseArgs <$> getArgs <*> getStdGen >>= id
The problem I have, parseArgs <$> getArgs <*> getStdGen is of type IO (IO ()), which I extract using (>>= id) which is of type Monad m => m (m b) -> m b. Is there a way to avoid requiring the "extraction" of the value while having just a single line function?
The easiest way would be with join:
main = join $ parseArgs <$> getArgs <*> getStdGen
Personally, I would prefer the form
main = join $ liftM2 parseArgs getArgs getStdGen
where
join :: Monad m => m (m a) -> m a
liftM2 :: Monad m => (a -> b -> r) -> m a -> m b -> m r
Or just use a do
main = do
args <- getArgs
gen <- getStdGen
parseArgs args gen
You can define an operator for this:
infixl 4 <&>
(<&>) :: Monad m => m (a -> m b) -> m a -> m b
f <&> x = f >>= (x >>=)
If you have a function of type
f :: Monad m => (a1 -> a2 -> ... -> an -> m b) -> m a1 -> m a2 -> ... -> m an -> m b
then you can write
fx :: Monad m => m b
fx = f <$> x1 <*> x2 <*> ... <&> xn
where each xi has type m ai.
In your case it would be simply
parseArgs <$> getArgs <&> getStdGen
You could pair up the arguments and put them through a single bind:
main = uncurry parseArgs =<< (,) <$> getArgs <*> getStdGen
This avoids the extraction from nested IO. Admittedly it's no shorter but I find it easier to think about.
It fits a general pattern of doTheWork =<< getAllTheInputs which might be the way you'd end up arranging things anyway, if the code was more complicated.
What follows is a series of examples/exercises upon Lenses (by Edward Kmett) in MonadState, based on the solution of Petr Pudlak to my previous question.
In addition to demonstrate some uses and the power of the lenses, these examples show how difficult it is to understand the type signature generated by GHCi. There is hope that in the future things will improve?
{-# LANGUAGE TemplateHaskell, RankNTypes #-}
import Control.Lens
import Control.Monad.State
---------- Example by Petr Pudlak ----------
-- | An example of a universal function that modifies any lens.
-- It reads a string and appends it to the existing value.
modif :: Lens' a String -> StateT a IO ()
modif l = do
s <- lift getLine
l %= (++ s)
-----------------------------------------------
The following comment type signatures are those produced by GHCi.
The other are adaptations from those of Peter.
Personally, I am struggling to understand than those produced by GHCi, and I wonder: why GHCi does not produce those simplified?
-------------------------------------------
-- modif2
-- :: (Profunctor p, MonadTrans t, MonadState s (t IO)) =>
-- (Int -> p a b) -> Setting p s s a b -> t IO ()
modif2 :: (Int -> Int -> Int) -> Lens' a Int -> StateT a IO ()
modif2 f l = do
s<- lift getLine
l %= f (read s :: Int)
---------------------------------------
-- modif3
-- :: (Profunctor p, MonadTrans t, MonadState s (t IO)) =>
-- (String -> p a b) -> Setting p s s a b -> t IO ()
modif3 :: (String -> Int -> Int) -> Lens' a Int -> StateT a IO ()
modif3 f l = do
s <- lift getLine
l %= f s
-- :t modif3 (\n -> (+) (read n :: Int)) == Lens' a Int -> StateT a IO ()
---------------------------------------
-- modif4
-- :: (Profunctor p, MonadTrans t, MonadState s (t IO)) =>
-- (t1 -> p a b) -> (String -> t1) -> Setting p s s a b -> t IO ()
modif4 :: (Bool -> Bool -> Bool) -> (String -> Bool) -> Lens' a Bool -> StateT a IO ()
modif4 f f2 l = do
s <- lift getLine
l %= f (f2 s)
-- :t modif4 (&&) (\s -> read s :: Bool) == Lens' a Bool -> StateT a IO ()
---------------------------------------
-- modif5
-- :: (Profunctor p, MonadTrans t, MonadState s (t IO)) =>
-- (t1 -> p a b) -> (String -> t1) -> Setting p s s a b -> t IO ()
modif5 :: (b -> b -> b) -> (String -> b) -> Lens' a b -> StateT a IO ()
modif5 f f2 l = do
s<- lift getLine
l %= f (f2 s)
-- :t modif5 (&&) (\s -> read s :: Bool) == Lens' a Bool -> StateT a IO ()
---------------------------------------
-- modif6
-- :: (Profunctor p, MonadState s m) =>
-- (t -> p a b) -> (t1 -> t) -> t1 -> Setting p s s a b -> m ()
modif6 :: (b -> b -> b) -> (c -> b) -> c -> Lens' a b -> StateT a IO ()
modif6 f f2 x l = do
l %= f (f2 x)
-- :t modif6 (&&) (\s -> read s :: Bool) "True" == MonadState s m => Setting (->) s s Bool Bool -> m ()
-- :t modif6 (&&) (\s -> read s :: Bool) "True"
---------------------------------------
-- modif7
-- :: (Profunctor p, MonadState s IO) =>
-- (t -> p a b) -> (String -> t) -> Setting p s s a b -> IO ()
modif7 :: (b -> b -> b) -> (String -> b) -> Lens' a b -> StateT a IO ()
modif7 f f2 l = do
s <- lift getLine
l %= f (f2 s)
-- :t modif7 (&&) (\s -> read s :: Bool) ==
-- :t modif7 (+) (\s -> read s :: Int) ==
---------------------------------------
p7a :: StateT Int IO ()
p7a = do
get
modif7 (+) (\s -> read s :: Int) id
test7a = execStateT p7a 10 -- if input 30 then result 40
---------------------------------------
p7b :: StateT Bool IO ()
p7b = do
get
modif7 (||) (\s -> read s :: Bool) id
test7b = execStateT p7b False -- if input "True" then result "True"
---------------------------------------
data Test = Test { _first :: Int
, _second :: Bool
}
deriving Show
$(makeLenses ''Test)
dataTest :: Test
dataTest = Test { _first = 1, _second = False }
monadTest :: StateT Test IO String
monadTest = do
get
lift . putStrLn $ "1) modify \"first\" (Int requested)"
lift . putStrLn $ "2) modify \"second\" (Bool requested)"
answ <- lift getLine
case answ of
"1" -> do lift . putStr $ "> Write an Int: "
modif7 (+) (\s -> read s :: Int) first
"2" -> do lift . putStr $ "> Write a Bool: "
modif7 (||) (\s -> read s :: Bool) second
_ -> error "Wrong choice!"
return answ
testMonadTest :: IO Test
testMonadTest = execStateT monadTest dataTest
As a family in the ML tradition, Haskell is specifically designed so that every toplevel binding has a most general type, and the Haskell implementation can and has to infer this most general type. This ensures that you can reuse the binding in as much places as possible. In a way, this means that type inference is never wrong, because whatever type you have in mind, type inference will figure out the same type or a more general type.
why GHCi does not produce those simplified?
It figures out the more general types instead. For example, you mention that GHC figures out the following type for some code:
modif2 :: (Profunctor p, MonadTrans t, MonadState s (t IO)) =>
(Int -> p a b) -> Setting p s s a b -> t IO ()
This is a very general type, because every time I use modif2, I can choose different profunctors p, monad transformers t and states s. So modif2 is very reusable. You prefer this type signature:
modif2 :: (Int -> Int -> Int) -> Lens' a Int -> StateT a IO ()
I agree that this is more readable, but also less generic: Here you decided that p has to be -> and t has to be StateT, and as a user of modif2, I couldn't change that.
There is hope that in the future things will improve?
I'm sure that Haskell will continue to mandate most general types as the result of type inference. I could imagine that in addition to the most general type, ghci or a third-party tool could show you example instantiations. In this case, it would be nice to declare somehow that -> is a typical profunctor. I'm not aware of any work in this direction, though, so there is not much hope, no.
Let's look at your first example:
modif :: Lens' a String -> StateT a IO ()
modif l = do
s <- lift getLine
l %= (++ s)
This type is simple, but it has also has a shortcoming: You can only use your function passing a Lens. You cannot use your function when you have an Iso are a Traversal, even though this would make perfect sense! Given the more general type that GHCi inferes, you could for example write the following:
modif _Just :: StateT (Maybe String) IO ()
which would append the read value only if that state was a Just, or
modif traverse :: StateT [String] IO ()
which would append the read value to all elements in the list. This is not possible with the simple type you gave, because _Just and traverse are not lenses, but only Traversals.
Why does this function have the type:
deleteAllMp4sExcluding :: [Char] -> IO (IO ())
instead of deleteAllMp4sExcluding :: [Char] -> IO ()
Also, how could I rewrite this so that it would have a simpler definition?
Here is the function definition:
import System.FilePath.Glob
import qualified Data.String.Utils as S
deleteAllMp4sExcluding videoFileName =
let dirGlob = globDir [compile "*"] "."
f = filter (\s -> S.endswith ".mp4" s && (/=) videoFileName s) . head . fst
lst = f <$> dirGlob
in mapM_ removeFile <$> lst
<$> when applied to IOs has type (a -> b) -> IO a -> IO b. So since mapM_ removeFile has type [FilePath] -> IO (), b in this case is IO (), so the result type becomes IO (IO ()).
To avoid nesting like this, you should not use <$> when the function you're trying to apply produces an IO value. Rather you should use >>= or, if you don't want to change the order of the operands, =<<.
Riffing on sepp2k's answer, this is an excellent example to show the difference between Functor and Monad.
The standard Haskell definition of Monad goes something like this (simplified):
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
However, this is not the only way the class could have been defined. An alternative runs like this:
class Functor m => Monad m where
return :: a -> m a
join :: m (m a) -> m a
Given that, you can define >>= in terms of fmap and join:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
ma >>= f = join (f <$> ma)
We'll look at this in a simplified sketch of the problem you're running into. What you're doing can be schematized like this:
ma :: IO a
f :: a -> IO b
f <$> ma :: IO (IO b)
Now you're stuck because you need an IO b, and the Functor class has no operation that will get you there from IO (IO b). The only way to get where you want is to dip into Monad, and the join operation is precisely what solves it:
join (f <$> ma) :: IO b
But by the join/<$> definition of >>=, this is the same as:
ma >>= f :: IO a
Note that the Control.Monad library comes with a version of join (written in terms of return and (>>=)); you could put that in your function to get the result you want. But the better thing to do is to recognize that what you're trying to do is fundamentally monadic, and thus that <$> is not the right tool for the job. You're feeding the result of one action to another; that intrinsically requires you to use Monad.
findM :: Monad m => (a -> m Bool) -> m [a] -> Maybe (m a)
I cannot implement it by myself. I could use some pointers
find looks like:
find f as = listToMaybe $ filter f as
so I tried:
findM f as = filterM f as >>= listToMaybe
but it doesnt work.
No. It is not possible. However, you can write a function with the signature:
findM :: Monad m => (a -> m Bool) -> m [a] -> m (Maybe a)
The reason it is not possible is because in order to figure out if the Maybe has constructor Just x or Nothing, you have to get the value out of the monad. Which means that the Maybe must be inside the monad at the end, since it is not possible in general to extract a value from inside a monad. (That is the whole point of monads, after all.)
For example, given your signature to findM, you could write some obviously incorrect functions:
findM :: Monad m => (a -> m Bool) -> m [a] -> Maybe (m a)
findM = magic
anyM :: Monad m => (a -> m Bool) -> m [a] -> Bool
anyM f = isJust . findM f
extractBool :: Monad m => m Bool -> Bool
extractBool = anyM id . liftM (:[])
The function extractBool is obviously impossible, so findM cannot have that signature.
Here is an implementation of findM:
findM :: Monad m => (a -> m Bool) -> [a] -> m (Maybe a)
findM _ [] = return Nothing
findM f (x:xs) = do y <- f x
if y then return (Just x) else findM f xs
I am not sure of a simpler way to implement it -- this seems fairly clean, and it works on infinite lists.
Changing the signature from using an m [a] to using [a] makes it more useful and easier to use. You'll quickly figure out why, if you implement both interfaces.
Try
findM :: Monad m => (a -> m Bool) -> [a] -> m (Maybe a)
findM p = foldr step (return Nothing)
where
step x r = do
b <- p x
if b then return (Just x) else r
This version only uses the predicate as much as it has to, whereas the filterM version uses it on every element. So for example:
ghci> let findM' p xs = filterM p xs >>= return . listToMaybe
ghci> let check x = putStrLn ("checking " ++ show x) >> doesDirectoryExist x
ghci> findM check ["test1", ".", "test2"]
checking "test1"
checking "."
Just "."
ghci> findM' check ["test1", ".", "test2"]
checking "test1"
checking "."
checking "test2"
Just "."
Others have already demonstrated that this isn't possible, however with a further constraint you can get a very similar function.
findM :: (Traversable m, Monad m) => (a -> m Bool) -> m [a] -> Maybe (m a)
findM p xs = Data.Traverse.sequence $ dietrichsFindM p xs
Not every Monad has a Traversable instance, but if it does this will work.
For the signature
findM :: Monad m => (a -> m Bool) -> m [a] -> m (Maybe a)
i suggest:
import Control.Monad
import Data.Maybe
findM :: Monad m => (a -> m Bool) -> m [a] -> m (Maybe a)
findM f m = m >>= filterM f >>= return . listToMaybe
or
findM :: Monad m => (a -> m Bool) -> m [a] -> m (Maybe a)
findM = ((return . listToMaybe =<<) .) . (=<<) . filterM
for a point-free style.