guard and pure in Haskell - haskell

I was reading Dynamic programming example, there is a code like this:
buy n = r!n
where r = listArray (0,n) (Just (0,0,0) : map f [1..n])
f i = do (x,y,z) <- attempt (i-6)
return (x+1,y,z)
`mplus`
do (x,y,z) <- attempt (i-9)
return (x,y+1,z)
`mplus`
do (x,y,z) <- attempt (i-20)
return (x,y,z+1)
attempt x = guard (x>=0) >> r!x
My question is how the attempt x = guard (x>=0) >> r!x works?
According to this Control.Monad source code,
guard True = pure ()
guard False = empty
pure :: a -> f a
m >> k = m >>= \_ -> k
so if x>0, then:
attempt x
= (guard True) >> (r!x) = (pure ()) >> (r!x)
= (pure ()) >>= \_ -> r!x = (f ()) >>= (\_ -> r!x)
hence f () should be of type m a (Maybe a in this case), but how does Haskell know what f is? f () may return empty since it has never been specified. (f means f in pure)
And if x<0, empty is not in Maybe, how can this still applied to >>=?

That's multiple questions in one, but let's see if I can make things a bit more clear.
How does Haskell know what f is when interpreting pure ()? pure is a typeclass method, so this simply comes from the instance declaration of the type we're in. This changed recently, so you may have to follow a different path to reach the answer, but the result ends up the same: pure for Maybe is defined as Just.
In the same way, empty is in Maybe, and is defined as Nothing.
You'll find out what typeclass provides those functions by typing :i pure or :i empty at a ghci prompt; then you can seek the instance declaration Maybe makes for them.
It is unfortunate from an SO point of view that this changed recently so there's no clear permanent answer without knowing the specific versions you're using. Hopefully this will settle soon.

In the last expression of your manual evaluation of attempt x you are mixing up types and values. pure :: a -> f a is not a definition; it is a type signature (note the ::). To quote it fully, the type of pure is:
GHCi> :t pure
pure :: Applicative f => a -> f a
Here, the f stands for any instance of Applicative, and the a for any type. In your case, you are working with the Maybe monad/applicative functor, and so f is Maybe. The type of pure () is Maybe (). (() :: () is a dummy value used when you are not interested in a result. The () in pure () is a value, but the () in Maybe () is a type -- the type of the () value).
We will continue from the last correct step in your evaluation:
(pure ()) >>= \_ -> r!x
how does Haskell know what [pure ()] is?
In a sense, it doesn't need to. The function which makes use of pure () here is (>>=). It has the following type:
GHCi> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
Setting m to Maybe, as in your case, we get:
Maybe a -> (a -> Maybe b) -> Maybe b
The type of the first argument is Maybe a, and so (>>=) is able to handle any Maybe a value, including pure (), regardless of whether it is a Just-something or Nothing. Naturally, it will handle Just and Nothing differently, as that is the whole point of the Monad instance:
(Just x) >>= k = k x
Nothing >>= _ = Nothing
We still have to complete the evaluation. To do so, we need to know how pure is defined for Maybe. We can find the definition in the Applicative instance of Maybe:
pure = Just
Now we can finally continue:
(pure ()) >>= \_ -> r!x
Just () >>= \_ -> r!x
(\_ -> r!x) () -- See the implementation of `(>>=)` above.
r!x

Related

Haskell instance of `bind` for a custom type

I'm trying to create an instance for bind operator (>>=) to the custom type ST a
I found this way to do it but I don't like that hardcoded 0.
Is there any way to implement it without having the hardcoded 0 and respecting the type of the function?
newtype ST a = S (Int -> (a, Int))
-- This may be useful to implement ">>=" (bind), but it is not mandatory to use it
runState :: ST a -> Int -> (a, Int)
runState (S s) = s
instance Monad ST where
return :: a -> ST a
return x = S (\n -> (x, n))
(>>=) :: ST a -> (a -> ST b) -> ST b
s >>= f = f (fst (runState s 0))
I often find it easier to follow such code with a certain type of a pseudocode rewrite, like this: starting with the
instance Monad ST where
return :: a -> ST a
return x = S (\n -> (x, n))
we get to the
runState (return x) n = (x, n)
which expresses the same thing exactly. It is now a kind of a definition through an interaction law that it must follow. This allows me to ignore the "noise"/wrapping around the essential stuff.
Similarly, then, we have
(>>=) :: ST a -> (a -> ST b) -> ST b
s >>= f = -- f (fst (runState s 0)) -- nah, 0? what's that?
--
-- runState (s >>= f) n = runState (f a) i where
-- (a, i) = runState s n
--
S $ \ n -> let (a, i) = runState s n in
runState (f a) i
because now we have an Int in sight (i.e. in scope), n, that will get provided to us when the combined computation s >>= f will "run". I mean, when it will runState.
Of course nothing actually runs until called upon from main. But it can be a helpful metaphor to hold in mind.
The way we've defined it is both the easiest and the most general, which is usually the way to go. There are more ways to make the types fit though.
One is to use n twice, in the input to the second runState as well, but this will leave the i hanging unused.
Another way is to flip the time arrow around w.r.t. the state passing, with
S $ \ n -> let (a, i2) = runState s i
(b, i ) = runState (f a) n
in (b, i2)
which is a bit weird to say the least. s still runs first (as expected for the s >>= f combination) to produce the value a from which f creates the second computation stage, but the state is being passed around in the opposite direction.
The most important thing to keep in mind is that your ST type is a wrapper around a function. What if you started your definition as (>>=) = \s -> \f -> S (\n -> ... )? It might be (ok, is) a bit silly to write separate lambdas for the s and f parameters there, but I did it to show that they're not really any different from the n parameter. You can use it in your definition of (>>=).

Can `(>>=)` be redeclared as `(a -> m b) -> m a -> m b`?

In Haskell Monad is declared as
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
return = pure
I was wondering if it is okay to redeclare the bind operator as
(>>=) :: (a -> m b) -> m a -> m b
?
Is it correct that the second declaration makes it clearer that (>>=) maps a function of type a -> m b to a function of type m a -> m b, while the original declaration makes less clear what it means?
Will that change of declaration make something from possible to impossible, or just require some change of using monad (which seems bearable to Haskell programmers)?
Thanks.
There's one reason why >>= tends to be more useful in practice than it's flipped counterpart =<<: it plays nicely with lambda notation. Namely, \ acts as a syntactic herald, so you can continue the computation without needing any parentheses. For instance,
do x <- [1..5]
y <- [10..20]
return $ x*y
can be rewritten very easily in terms of >>= as
[1..5] >>= \x -> [10..20] >>= \y -> return $ x*y
You still have much the same “imperative flow” feel as with the do version.
Whereas with =<< it would require awkward parentheses and seem to read backwards:
(\x -> (\y -> return $ x*y) =<< [10..20]) =<< [1..5]
Ok, you might say this feels more like function application. But where that is useful, it is often more poignant to use only the applicative functor interface rather than the monadic one:
(\x y -> x*y) <$> [1..5] <*> [10..20]
or short
(*) <$> [1..5] <*> [10..20]
Note that (<*>) :: f (a->b) -> f a -> f b has essentially the order of =<< that you propose, just with the a-> inside the functor rather than outside.

Is there a "chain" monad function in Haskell?

Explain about a "duplicate"
Someone point to Is this a case for foldM? as a possible duplicate. Now, I have a strong opinion that, two questions that can be answered with identical answers are not necessarily duplicates! "What is 1 - 2" and "What is i^2" both yields "-1", but no, they are not duplicate questions. My question (which is already answered, kind of) was about "whether the function iterateM exists in Haskell standard library", not "How to implement a chained monad action".
The question
When I write some projects, I found myself writing this combinator:
repeatM :: Monad m => Int -> (a -> m a) -> a -> m a
repeatM 0 _ a = return a
repeatM n f a = (repeatM (n-1) f) =<< f a
It just performs a monadic action n times, feeding the previous result into the next action. I tried some hoogle search and some Google search, and did not find anything that comes with the "standard" Haskell. Is there such a formal function that is predefined?
You can use foldM, e.g.:
import Control.Monad
f a = do print a; return (a+2)
repeatM n f a0 = foldM (\a _ -> f a) a0 [1..n]
test = repeatM 5 f 3
-- output: 3 5 7 9 11
Carsten mentioned replicate, and that's not a bad thought.
import Control.Monad
repeatM n f = foldr (>=>) pure (replicate n f)
The idea behind this is that for any monad m, the functions of type a -> m b form the Kleisli category of m, with identity arrows
pure :: a -> m a
(also called return)
and composition operator
(<=<) :: (b -> m c) -> (a -> m b) -> a -> m c
f <=< g = \a -> f =<< g a
Since were actually dealing with a function of type a -> m a, we're really looking at one monoid of the Kleisli category, so we can think about folding lists of these arrows.
What the code above does is fold the composition operator, flipped, into a list of n copies of f, finishing off with an identity as usual. Flipping the composition operator actually puts us into the dual category; for many common monads, x >=> y >=> z >=> w is more efficient than w <=< z <=< y <=< x; since all the arrows are the same in this case, it seems we might as well. Note that for the lazy state monad and likely also the reader monad, it may be better to use the unflipped <=< operator; >=> will generally be better for IO, ST s, and the usual strict state.
Notice: I am no category theorist, so there may be errors in the explanation above.
I find myself wanting this function often, I wish it had a standard name. That name however would not be repeatM - that would be for an infinite repeat, like forever if it existed, just for consistency with other libraries (and repeatM is defined in some libraries that way).
Just as another perspective from the answers already given, I point out that (s -> m s) looks a bit like an action in a State monad with state type s.
In fact, it is isomorphic to StateT s m () - an action which returns no value, because all the work it does is encapsulated in the way it changes the state. In this monad, the function you wanted really is replicateM. You can write it this way in haskell although it probably looks uglier than just writing it directly.
First convert s -> m s to the equivalent form which StateT uses, adding the information-free (), using liftM to map a function over the return type.
> :t \f -> liftM (\x -> ((),x)) . f
\f -> liftM (\x -> ((),x)) . f :: Monad m => (a -> m t) -> a -> m ((), t)
(could have used fmap but the Monad constraint seems clearer here; could have used TupleSections if you like; if you find do notation easier to read it is simply \f s -> do x <- f s; return ((),s) ).
Now this has the right type to wrap up with StateT:
> :t StateT . \f -> liftM (\x -> ((),x)) . f
StateT . \f -> liftM (\x -> ((),x)) . f :: Monad m => (s -> m s) -> StateT s m ()
and then you can replicate it n times, using the replicateM_ version because the returned list [()] from replicateM would not be interesting:
> :t \n -> replicateM_ n . StateT . \f -> liftM (\x -> ((),x)) . f
\n -> replicateM_ n . StateT . \f -> liftM (\x -> ((),x)) . f :: Monad m => Int -> (s -> m s) -> StateT s m ()
and finally you can use execStateT to go back to the Monad you were originally working in:
runNTimes :: Monad m => Int -> (s -> m s) -> s -> m s
runNTimes n act =
execStateT . replicateM_ n . StateT . (\f -> liftM (\x -> ((),x)) . f) $ act

On the signature of >>= Monad operator

This is the signature of the well know >>= operator in Haskell
>>= :: Monad m => m a -> (a -> m b) -> m b
The question is why type of the function is
(a -> m b)
instead of
(a -> b)
I would say the latter one is more practical because it allows straightforward integration of existing "pure" functions in the monad being defined.
On the contrary, it seems not difficult to write a general "adapter"
adapt :: (Monad m) => (a -> b) -> (a -> m b)
but anyway I regard more probable that you already have (a -> b) instead of (a -> m b).
Note. I explain what I mean by "pratical" and "probable".
If you haven't defined any monad in a program yet, then, the functions you have are "pure" (a -> b) and you will have 0 functions of the type (a -> m b) just because you have not still defined m. If then you decide to define a monad m it comes the need of having new a -> m b functions defined.
Basically, (>>=) lets you sequence operations in such a way that latter operations can choose to behave differently based on earlier results. A more pure function like you ask for is available in the Functor typeclass and is derivable using (>>=), but if you were stuck with it alone you'd no longer be able to sequence operations at all. There's also an intermediate called Applicative which allows you to sequence operations but not change them based on the intermediate results.
As an example, let's build up a simple IO action type from Functor to Applicative to Monad.
We'll focus on a type GetC which is as follows
GetC a = Pure a | GetC (Char -> GetC a)
The first constructor will make sense in time, but the second one should make sense immediately—GetC holds a function which can respond to an incoming character. We can turn GetC into an IO action in order to provide those characters
io :: GetC a -> IO a
io (Pure a) = return a
io (GetC go) = getChar >>= (\char -> io (go char))
Which makes it clear where Pure comes from---it handles pure values in our type. Finally, we're going to make GetC abstract: we're going to disallow using Pure or GetC directly and allow our users access only to functions we define. I'll write the most important one now
getc :: GetC Char
getc = GetC Pure
The function which gets a character then immediately considers is a pure value. While I called it the most important function, it's clear that right now GetC is pretty useless. All we can possibly do is run getc followed by io... to get an effect totally equivalent to getChar!
io getc === getChar :: IO Char
But we'll build up from here.
As stated at the beginning, the Functor typeclass provides a function exactly like you're looking for called fmap.
class Functor f where
fmap :: (a -> b) -> f a -> f b
It turns out that we can instantiate GetC as a Functor so let's do that.
instance Functor GetC where
fmap f (Pure a) = Pure (f a)
fmap f (GetC go) = GetC (\char -> fmap f (go char))
If you squint, you'll notice that fmap affects the Pure constructor only. In the GetC constructor it just gets "pushed down" and deferred until later. This is a hint as to the weakness of fmap, but let's try it.
io getc :: IO Char
io (fmap ord getc) :: IO Int
io (fmap (\c -> ord + 1) getc) :: IO Int
We've gotten the ability to modify the return type of our IO interpretation of our type, but that's about it! In particular, we're still limited to getting a single character and then running back to IO to do anything interesting with it.
This is the weakness of Functor. Since, as you noted, it deals only with pure functions it gets stuck "at the end of a computation" modifying the Pure constructor only.
The next step is Applicative which extends Functor like this
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
In other words it extends the notion of injecting pure values into our context and allowing pure function application to cross over the data type. Unsurprisingly, GetC instantiates Applicative too
instance Applicative GetC where
pure = Pure
Pure f <*> Pure x = Pure (f x)
GetC gof <*> getcx = GetC (\char -> gof <*> getcx)
Pure f <*> GetC gox = GetC (\char -> fmap f (gox char))
Applicative allows us to sequence operations and that might be clear from the definition already. In fact, we can see that (<*>) pushes character application forward so that the GetC actions on either side of (<*>) get performed in order. We use Applicative like this
fmap (,) getc <*> getc :: GetC (Char, Char)
and it allows us to build incredibly interesting functions, much more complex than just Functor would. For instance, we can already form a loop and get an infinite stream of characters.
getAll :: GetC [Char]
getAll = fmap (:) getc <*> getAll
which demonstrates the nature of Applicative being able to sequence actions one after another.
The problem is that we can't stop. io getAll is an infinite loop because it just consumes characters forever. We can't tell it to stop when it sees '\n', for instance, because Applicatives sequence without noticing earlier results.
So let's go the final step an instantiate Monad
instance Monad GetC where
return = pure
Pure a >>= f = f a
GetC go >>= f = GetC (\char -> go char >>= f)
Which allows us immediately to implement a stopping getAll
getLn :: GetC String
getLn = getc >>= \c -> case c of
'\n' -> return []
s -> fmap (s:) getLn
Or, using do notation
getLn :: GetC String
getLn = do
c <- getc
case c of
'\n' -> return []
s -> fmap (s:) getLn
So what gives? Why can we now write a stopping loop?
Because (>>=) :: m a -> (a -> m b) -> m b lets the second argument, a function of the pure value, choose the next action, m b. In this case, if the incoming character is '\n' we choose to return [] and terminate the loop. If not, we choose to recurse.
So that's why you might want a Monad over a Functor. There's much more to the story, but those are the basics.
The reason is that (>>=) is more general. The function you're suggesting is called liftM and can be easily defined as
liftM :: (Monad m) => (a -> b) -> (m a -> m b)
liftM f k = k >>= return . f
This concept has its own type class called Functor with fmap :: (Functor m) => (a -> b) -> (m a -> m b). Every Monad is also a Functor with fmap = liftM, but for historical reasons this isn't (yet) captured in the type-class hierarchy.
And adapt you're suggesting can be defined as
adapt :: (Monad m) => (a -> b) -> (a -> m b)
adapt f = return . f
Notice that having adapt is equivalent to having return as return can be defined as adapt id.
So anything that has >>= can also have these two functions, but not vice versa. There are structures that are Functors but not Monads.
The intuition behind this difference is simple: A computation within a monad can depend on the results of the previous monads. The important piece is (a -> m b) which means that not just b, but also its "effect" m b can depend on a. For example, we can have
import Control.Monad
mIfThenElse :: (Monad m) => m Bool -> m a -> m a -> m a
mIfThenElse p t f = p >>= \x -> if x then t else f
but it's not possible to define this function with just Functor m constraint, using just fmap. Functors only allow us to change the value "inside", but we can't take it "out" to decide what action to take.
As others have said, your bind is the fmap function of the Functor class, a.k.a <$>.
But why is it less powerful than >>=?
it seems not difficult to write a general "adapter"
adapt :: (Monad m) => (a -> b) -> (a -> m b)
You can indeed write a function with this type:
adapt f x = return (f x)
However, this function is not able to do everything that we might want >>='s argument to do. There are useful values that adapt cannot produce.
In the list monad, return x = [x], so adapt will always return a single-element list.
In the Maybe monad, return x = Some x, so adapt will never return None.
In the IO monad, once you retrieved the result of an operation, all you can do is compute a new value from it, you can't run a subsequent operation!
etc. So in short, fmap is able to do fewer things than >>=. That doesn't mean it's useless -- it wouldn't have a name if it was :) But it is less powerful.
The whole 'point' of the monad really (that puts it above functor or applicative) is that you can determine the monad you 'return' based on the values/results of the left hand side.
For example, >>= on a Maybe type allows us to decide to return Just x or Nothing. You'll note that using functors or applicative, it is impossible to "choose" to return Just x or Nothing based on the "sequenced" Maybe.
Try implementing something like:
halve :: Int -> Maybe Int
halve n | even n = Just (n `div` 2)
| otherwise = Nothing
return 24 >>= halve >>= halve >>= halve
with only <$> (fmap1) or <*> (ap).
Actually the "straightforward integration of pure code" that you mention is a significant aspect of the functor design pattern, and is very useful. However, it is in many ways unrelated to the motivation behind >>= --- they are meant for different applications and things.
I had the same question for a while and was thinking why bother with a -> m b once mapping a -> b to m a -> m b looks more natural. This is simialr to asking "why we need a monad given the functor".
The little answer that I give to myself is that a -> m b accounts for side-effects or other complexities that you would not capture with function a -> b.
Even better wording form here (highly recommend):
monadic value M a can itself be seen as a computation. Monadic functions represent computations that are, in some way, non-standard, i.e. not naturally supported by the programming language. This can mean side effects in a pure functional language or asynchronous execution in an impure functional language. An ordinary function type cannot encode such computations and they are, instead, encoded using a datatype that has the monadic structure.
I'd put emphasis on ordinary function type cannot encode such computations, where ordinary is a -> b.
I think that J. Abrahamson's answer points to the right reason:
Basically, (>>=) lets you sequence operations in such a way that latter operations can choose to behave differently based on earlier results. A more pure function like you ask for is available in the Functor typeclass and is derivable using (>>=), but if you were stuck with it alone you'd no longer be able to sequence operations at all.
And let me show a simple counterexample against >>= :: Monad m => m a -> (a -> b) -> m b.
It is clear that we want to have values bound to a context. And perhaps we will need to sequentially chain functions over such "context-ed values". (This is just one use case for Monads).
Take Maybe simply as a case of "context-ed value".
Then define a "fake" monad class:
class Mokad m where
returk :: t -> m t
(>>==) :: m t1 -> (t1 -> t2) -> m t2
Now let's try to have Maybe be an instance of Mokad
instance Mokad Maybe where
returk x = Just x
Nothing >>== f = Nothing
Just x >>== f = Just (f x) -- ????? always Just ?????
The first problem appears: >>== is always returning Just _.
Now let's try to chain functions over Maybe using >>==
(we sequentially extract the values of three Maybes just to add them)
chainK :: Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int
chainK ma mb mc = md
where
md = ma >>== \a -> mb >>== \b -> mc >>== \c -> returk $ a+b+c
But, this code doesn't compile: md type is Maybe (Maybe (Maybe Int)) because every time >>== is used, it encapsulates the previous result into a Maybe box.
And on the contrary >>= works fine:
chainOK :: Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int
chainOK ma mb mc = md
where
md = ma >>= \a -> mb >>= \b -> mc >>= \c -> return (a+b+c)

How to mix Haskell monadic and pure filters in a piping fashion?

In a previous question, I tried to ask about how to mix pure and monadic functions by piping them together, but because I may have worded my question wrong and my example was too simplistic, I think the discussion went the wrong direction, so I think I'll try again.
Here is an example function that mixes pure and monadic filters. In this example, there are some pure filters sequenced in-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 benefit of writing it this way, where pure functions are mixed in using return, is 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 the returns to make it cleaner. I had the idea of using 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 operator precedence problems. Does this already exist? It is similar to <$> but the "other direction". If not, how do I make this operator work?
More generally, is there a good way to write code in this piping fashion, or need I settle for fmaps and temporary variables like as described in my previous question?
Ugh. As simple as this:
infixl 1 |>=
(|>=) = flip fmap
findFiles target =
getDirectoryContents target |>=
filter (not . (=~ "[0-9]{8}\\.txt$")) |>=
filter (=~ "\\.txt$") >>=
filterM doesFileExist >>=
mapM canonicalizePath
Seconding DiegoNolan, there's no prize for the pointest-free code and no shame in using do-notation, binding intermediate values with either a monadic assignment (x <- ...) or a good old-fashioned let. The heirs to your code will thank you.
That said, if you can't bear points, you might be a category theorist. Seriously, you can take a page from John Hughes (see Programming with Arrows) and write your pipeline like this:
import Control.Arrow
findFiles = runKleisli $
Kleisli getDirectoryContents >>>
arr (filter (not . (=~ "[0-9]{8}\\.txt$"))) >>>
arr (filter (=~ "\\.txt$")) >>>
Kleisli (filterM doesFileExist) >>>
Kleisli (mapM canonicalizePath)
This is probably a little more principled than monkeying around with one's own special bind operators, but still uglier than the plain pointed style if you ask me. De gustibus non est disputandum, as the Romans always used to say about garum.
Use (<$>), also known as fmap, for mapping pure functions into a functor. Most monads have instances of functors. If they don't have one then you can use liftM
Looking at the types
liftM :: Monad m => (a -> b) -> m a -> m b
(<$>) :: Functor f => (a -> b) -> f a -> f b
Yours would look like this (haven't checken in ghc).
findFiles target =
((filter (not . (=~ "[0-9]{8}\\.txt$")) .
filter (=~ "\\.txt$") ) <$>
getDirectoryContents target) >>=
filterM doesFileExist >>=
mapM canonicalizePath
But at this point you're probably just better off using do notation and let.
You'll want a few extra operators, one to handle each case
Monad -> Monad
Monad -> Pure
Pure -> Monad
Pure -> Pure
You already have the Monad -> Monad case (>>=), and as I described in my answer to your last question, you could use |>= for the Pure -> Monad case, but you'll still need Monad -> Pure one. That's going to be tricky, since the only type-safe way to do it is by having that operator transform your pure function into a monadic one. I'd recommend the following set of operators
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)
Using the convention that > means "monad" and | means "pure", and all end with = meaning "to function". Hopefully the type signatures will make sense with the implementations:
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
And an example
test :: IO ()
test =
getLine >|=
filter (/= 't') ||=
map toUpper >>=
putStrLn
> test
testing
ESING
>
This is also equivalent to
test :: IO ()
test =
getLine >|=
filter (/= 't') >|=
map toUpper >>=
putStrLn
But the extra ||> combination would let you actually compose those functions, which has a different implementation under the hood than feeding them through monadic actions.
However, I would still urge you to use the idiomatic way of doing this by using fmap, do notation, and temporary variables. It'll be much clearer to anyone else that looks at the code, and that includes you in 2 months' time.

Resources