My first functional language was APL; I later moved from C to Standard ML. I loved Scheme till I realized I was camping, it was getting late, and I needed to build a shelter from Y-shaped twigs. Somehow OCaml never took for me, but reading a few of its source code modules was an efficient crash course for coding in a functional language.
What is the dream that these languages and Haskell aspires to realize? We want to slice computations in arbitrary ways and manipulate the parts as algebra. This dream of many since the first Lisp meets resistance from those who don't love algebra, but one cannot fault the aspiration: Find a way we can all code with more leverage, less effort.
Pretend Haskell is as small as APL or Scheme. Ignore those who scoff at your primitive avoidance of an emerging Haskell pattern.
I highly recommend Simon Marlow's "Parallel and Concurrent Programming in Haskell". I don't know any language that handles parallel as gracefully, and one can read this book without ever seeing an >=> or <=<.
In normal function composition, let's say you have function `tostr` that converts a boolean to string, and function `strlen` that computes the length of the string.
They have the ff. signatures:
tostr :: boolean -> string
strlen :: string -> number
If you compose these two functions, where you `tostr` then `strlen`, then the resulting function will have the ff. signature:
composed :: boolean -> number
Now let's suppose that `tostr` and `strlen` both return a Maybe monad, with the ff. signatures:
tostr :: boolean -> Maybe string
strlen :: string -> Maybe number
This is where the fish operator helps, because if we compose it normally it doesn't work because `strlen` expects a string, not a Maybe string. The fish operator will automatically unwrap the value, resulting in the ff. signature:
mcomposed :: boolean -> Maybe number
f = h <=< g
over f x = do
y <- g x
h ySimplest example, I have would be to read something from a file and apply some function `f :: String -> String` to the contents of the file, and return an IO String again. Let's say that overarching function is called readFileAndProcess, then it could be defined like
`readFileAndProcess :: (String-> String) -> String -> IO String`
`readFileAndProcess f fileName = putStrLn . f <=< (readFile fileName)`