Functional programming, APL and UNIX pipes
porg.es
porg.es
so if I look at f(b(c(d(e))))) it is the same as 'cat e | d | c | b | f'
Where 'e' is the initial input data and the other letters are the functions transforming e in successive steps to the desired output.
The analogy probably breaks down pretty quickly, but for simple examples it seems to hold.
Is there a programming language that works in this left-to-right pipelined fashion?
user=> (->> 10 (+ 2) (* 5))
60
user=> (* 5 (+ 2 10))
60
http://debasishg.blogspot.com/2010/04/thrush-in-clojure.htmlNeat, never knew that was in there. I really should do more reading before diving in to using something. Bad habit.
It would be nice to see two fair sized chunks of code side by side, one in each style and then to ask a panel of programmers which is the more readable of the two.
I'm beginning to slowly 'grok' functional code, I can read it a bit easier now than two months ago (exposure), but I still feel like with olives that it is an acquired taste.
I hope I'll grow over that, it definitely doesn't feel natural yet, and it takes me much longer to understand a piece of functional code than it does when I look at 'imperative' code.
This must have something to do with the lack of side effects.
For some fun I coded up a small site in PHP in a functional style (I can see a lot of HN'ers gouge out their eyeballs at this sentence, apologies, it was just an experiment), and it gave me a lot more practice in reading functional code.
But the side effect free trick had - pun intended - a side effect by itself, the code worked the first time out after writing it, and that's unusual for a 1500 lines or so project, once the minor syntactical problems were dealt with (mostly quoting issues).
That really was unexpected, if there is any concrete explanation for that (other than blind luck) I'd like to hear it, and if this is more common in the 'functional world'.
http://news.ycombinator.com/item?id=1361382
The language I'm compiling does this, but with non-linear "pipe" systems. It's a bizarre mongrel of functional, dataflow, OO and declarative (as per Prolog), and revisits Alan Kay's idea that OO is about the messages, not the objects. On steroids.
It uses the left to right version, so function application is (args).(func) although that's not the syntax. In particular, using Lisp-like notation, addition is written similarly to (3 4 +) where 3 is sent to the tail of the list. the tail of the list is 4 sent to +, which creates a function that adds 4 to its argument. Thus (4 +) is a function in its own right.
There are interesting parallels with continuations and the like, but we've found some problems with the underlying theoretical basis and are re-working some of the ideas.
Of course it's probably isomorphic to an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp, but there you go.
Never heard of 'factor' yet, I'll have a look at it.
Thanks!
http://factor-language.blogspot.com/2009/09/survey-of-domain...
pretty much the same as lisp's. MACRO: defines a lisp style macro, http://docs.factorcode.org/content/word-MACRO__colon__%2Cmac... (MACRO:: defines a macro with local variables)
SYNTAX: defines a 'parsing word' http://docs.factorcode.org/content/article-parsing-words.htm... which I think is basically the same as a lisp Reader Macro
Though you don't seem to need macros very often because you tend to pass things around as lambdas, cond for instance is implemented as a normal function.
I went so far as defining | as a noop (I think it was SYNTAX: | ; )
That way I could do stuff like [1, 3, 4] | first | dup
The APL/J/K languages are built around a construct very similar to this, so the OP's reference to APL is apt. You really should check out J or K if you haven't yet. J comes with a marvelous set of tutorials. K comes with nothing. :)
(But K is a work of genius, in my opinion. A pinnacle.)
But if you go and learn K or Q and you've got good C chops, you can absolutely get a job in Manhattan or Chicago (must be willing to work long hours with little support, build almost all of your own tools, and handle the constant pressure of making everything work perfectly the first time).
[[10..20], [50..100]] >>= id >>= classify
The only thing you can't directly do with monads is the fold, since the fold breaks you out of the monad. Also, you could use arrows: myCounter = ( concat >>> (map classify) >>> (foldr getCounts (0,0,0)) )
Then myCounter [ [10..20], [50..60]] is the function you want. (return [[10..20], [50..100]] >>= concat >>= classify >>= foldr getCount (0,0,0))
Just use the identity monad rather than the list monad.Output from one becomes the input to another component. Most takes on it that I've seen (like the two above) slap a graphical interface on top for flexibility.
let double_then_sum a_list =
a_list
|> List.map ((*) 2)
|> List.fold (+) 0In this case the "." is functioning sort of like the pipe.
something like [1, 2, 3].first().add(2)