A composed function can be thought of as being more generalized than a pipeline, as a pipeline needs a value to start with while a composed function receives an argrument directly.
let clickHandler (value: string) = value |> parse |> sanitize |> serialize |> sendPayload
The function above takes an argument, then passes that argument forward into a pipeline.
let clickHandler = sendPayload << serialize << sanitize << parse
By composing a function, we accept the argument directly into the first function in the composition. We have no need to have an intermediate function body whose only purpose is to pass forward an argument.
With a library like Ramda where pipe is essentially the same as compose, there doesn't appear to be a significant reason to use one over the other. Perhaps semantics is most important in that case. Where
pipe can be used to highlight that a value is changing over the course of many operations, and compose can be used to highlight the fact that a new, named function has been composed from many functions.