Unix pipelines actually helped me make sense of Haskell's monad.
Unix pipelines actually helped me make sense of Haskell's monad.
a; b # Execute command b after a. The result of a is not used by b.
In Haskell: a >> b # Run function b after a. The result of a is not used by b.
In Unix: a | b # Execute command b after a. The result of a is used by b.
In Haskell: a >>= b # Run function b after a. The result of a is used by b.Edit: I might be misinterpreting your use of "execute _after_". You may not mean "after the completion of a" but instead "operating on the results of a, asynchronously" in which case, apologies.
main = getArgs >>= processData >>= displayData
main is in the IO monad. The >>= function takes a value wrapped in a monad and a function which accepts a value and returns a value wrapped in the same type of monad, and returns the same monad-wrapped value the function did. It can be used as an infix operator because Haskell allows that if you specify precedence, in which case its left side is the first argument (the value-in-a-monad) and its right side is the second argument (the function).The (imaginary) function getArgs takes no arguments and returns a list of command-line arguments wrapped in an IO monad. The first >>= takes that list and feeds it into the function on its right, processData, which accepts it and returns some other value in an IO monad, which the third >>= accepts and feeds into displayData, which accepts it and returns nothing (or, technically, IO (), the unit (empty) value wrapped in an IO monad) which is main's return value.
See? Everything is still function application, but the mental model becomes feeding data through a pipeline of functions, each of which operates on the data and passes it along.
For pipelines to be monadic, their structure must be able to change depending on the result on the part of pipeline. The type of binding operator hints at it: (>>=) :: m a -> (a -> m b) -> m b.
The second argument receives result of first computation and computes the way overall result (m b) will be computed.
As for program composition, I would like to add gstreamer to the mix: it allows for DAG communication between programs.