Chaining vs. Nesting
frankschmitt.org
frankschmitt.org
Or you can think of it like this:
netstat_n >>= grep "tcp" >>= flip awk 5 >>= sort >>= uniq_c >>= sort_n
See also: http://okmij.org/ftp/Computation/monadic-shell.html
Pipes and method chaining "feel" the same. They're both expressions, but with the property that you could rewrite them as what looks like a series of statements without changing the semantics (although possibly changing the efficiency). e.g.
usual_suspects.director.surname
has the same value ("Singer") as person = usual_suspects.director
surname = person.surname
surname
and cat /etc/passwd | grep root | cut -d: -f6
outputs the same thing (root's home directory) as cat /etc/passwd > tmp1
grep root tmp1 > tmp2
cut -d: -f6 tmp2 > tmp3
cat tmp3
They feel the same because they're both monadic (at least when the chained methods or piped processes behave in certain 'normal' ways). '.' (the method invocation operator) and '|' (the pipe operator) are monadic combinators, just like Haskell's >>= and F#'s |>. And a monadic combinator is just a generalisation of function composition.Monads are going to become a lot more important in the next few years, as programming languages get more expressive and capable of more abstraction. LINQ in C# and VB is a great example - while it looks like they've baked in language support for lots of concepts (querying databases and XML, for data parallelism and for event-driven programming), what they've really done is recognised that those are all forms of monadic computation, baked in language support for one thing (monadic expressions), then implemented those concepts as libraries that any user could have written. (Erik Meijer and Brian Beckman on MS's Channel 9 explain this really nicely.)
(-> netstat_n (grep "tcp") (awk 5) sort uniq_c sort_n print)
Using the example from the article, one could do it as:
sort -n $ uniq -n $ sort $ awk '{ print $5}' $ grep tcp $ netstat -n
I.e essentially the same but in reverse order.
Your example would rather read something close to
sort -n . uniq -n . sort . awk '{ print $5}' . grep tcp . netstat -n
(And `$ argument' at the end, if there was one.)What I like is how ($) generalises to (<$>) for applicative functors, so you can write
succ $ 5 -- -> 6
succ <$> Nothing -- -> Nothing
succ <$> Just 5 -- -> Just 6
succ <$> [1, 2, 3] -- -> [2, 3, 4]
Who needs "for" loops? :)Haskel side-steps this problem in a neat way - you can take advantage of delayed evaluation and give names to both arguments of the ternary expression so each individual piece looks linear. So your "tree" is broken down line by line:
x=...
y=...
z=f?x:y
which is as neat and easy to read as a chain, but is more powerful because it can express a tree.Once could enforce this sort of reader-friendly style by forbidding expressions in ternary operators.
-- from an interview with Alfred Aho, one of the creators of AWK
http://groups.google.com/group/comp.lang.functional/msg/8476...
The machine's panel switches are enough of a pain when programming in the absolute -- give a shell user a teletype at least!
Pipes are a combination of dataflow programming and monadic computation, and are naturally parallelizable, functional and generally pipe processes don't share mutable state. All these concepts are highly relevant to modern programming.
You can argue that HTML has inherent syntax and semantics, but of course server and client can have slightly different ideas, and it all still works, mostly. The same is largely true of shell programming using pipes: different stages in the pipe expect certain formats, for regular expression or field extraction, and format pasting together, etc. The format is easy to eyeball and easy to test on the shell REPL, so in practice the problems aren't large.