6 ms·
Chaining vs. Nesting
- frognibble 17y agoHere's the example from the article written using the Clojure thread macro: (-> netstat_n (grep "tcp") (awk 5) sort uniq_c sort_n print)
- camccann 17y agoFunction composition and currying seem like a more natural translation to me. 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 http://okmij.org/ftp/Computation/monadic-shell.html
- samstokes 17y agoGlad someone posted that link, or I was going to :) 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.)
- pvg 17y agoComparing a model in which the contract is 'everything is a stream of characters' to function application and composition in just about any programming language seems more than a little clueless.
- camccann 17y ago"I think there's a connection. Certainly in the early days of Unix, pipes facilitated function composition on the command line. You could take an input, perform some transformation on it, and then pipe the output into another program. (...) [The mathematical approach of function composition is] exactly what I mean. (...) I think [that mathematical formalism] was right there from the start. Doug McIlroy, at least in my book, deserves the credit for pipes. He thought like a mathematician and I think he had this connection right from the start. I think of the Unix command line as a prototypical functional language." -- from an interview with Alfred Aho, one of the creators of AWK http://books.google.com/books?id=yB1WwURwBUQC&lpg=PT120&ots=-BUH9ErkdB&pg=PT120#v=onepage&q=&f=false http://books.google.com/books?id=yB1WwURwBUQC&lpg=PT120&...
- pvg 17y agoIt's 2010, not the 'early days of Unix'. Yes there is a connection, a useful one, if you are sitting in front of a PDP-8. http://groups.google.com/group/comp.lang.functional/msg/84763c6d9185178b?dmode=source&hl=en http://groups.google.com/group/comp.lang.functional/msg/8476... http://regex.info/blog/2006-09-15/247#comment-3085 http://regex.info/blog/2006-09-15/247#comment-3085
- blasdel 17y agoI don't think unix pipes in the shell would be very useful to someone sitting in front of a PDP-8 The machine's panel switches are enough of a pain when programming in the absolute -- give a shell user a teletype at least!
- pvg 17y agoA bit of a hyperbole, granted. Pipes -> any kind of vaguely modern runtime (clojure? srsly?) is a profoundly nonsensical comparison though.
- DenisM 17y agoI don't think ternary expression is ugly by itself, it's only ugly if parameters themselves are chains beacuse then you're back to having a tree. Which is the fundamental problem - chaining is not good for branches, so any branches send you back to nesting. On the higher level theproblem is that you are trying to express a tree as a list - ain't going to work. 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.
- voxcogitatio 17y agoI've seen an interesting way to do method chaining in haskell: The '$' operator: Basically it's a low-priority right-associative binary operator that applies the function to the left to everything to the right. 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.
- eru 17y ago($) helps to avoid some parens. The (.) is more common to change functions, and closely related. ($) does not really compose functions, it is function application and right associative. 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.)
- samstokes 17y ago($) is actually the least interesting operator in the world :) But as you say, its parsing precedence makes it useful (because it lets you skip parens). 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? :)
- eru 17y agoI agree. Though I'd say id or const or even less interesting than ($).
- eru 17y ago(`to change functions' should have been `to chain functions'.)
- Avshalom 17y agoI remember first running into this point with http://xahlee.org/UnixResource_dir/writ/notations.html http://xahlee.org/UnixResource_dir/writ/notations.html and I'm sure Slava or somebody has pointed out that in postfix languages like Factor and Forth everything is essentially chained which very naturally leads to a function composition style. And that despite the "reverse" nature of RPN it often ends up being "forward", witness in Factor REPL actually being REPL instead of L(P(E(R))).