it's just composition - it does one after the other (from the right) - so
(putStr . showInterface) n
is just
putStr (showInterface n)
so in a sense showInterface
is called first
please note that your understanding is a bit flawed here - ns
is not the monad here that matters (it's a list - it's the Foldable
part in the newer versions of mapM_
) - the monad that matters is IO
and mapM_
accepts a monadic action and a list of things to put into that action (one by one) - so here every item in ns
is put into the action
putStr . showInterface
and of course each one will first be used int the pure function showInterface
(which obviously produces a string) and then be printed to stdout
using putStr
(this is the monadic action that needs the mapM_
)
if in doubt check the signature
If you want to find out which part matters look at the signature
In case of mapM_
it's:
mapM_ :: (Monad m, Foldable t) => (a -> m b) -> t a -> m ()
forget the Foldable t
here - it's your list:
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
now what can the m
be?
Well you use putStr . showInterface
for the first argument - now I don't know exactly what showInterface
is - but as you compose it with putStr
the resulting type should be something like
putStr . showInterface :: SomeInterfaceType -> IO ()
and now you should see that: m ~ IO
, a ~ SomeInterfaceType
and b ~ ()
- notice that the monad is IO
! - and you get:
mapM_ (putStr . showInterface) :: [SomeInterfaceType] -> IO ()