Hopping Through Pipes and Closures #
Again, just an example of Ruby’s flexibility.
class IO def | proc proc[self] end end
Pass a File or Socket through a chain of procs with a pipe!
wc = proc { |io| io.inject(0) { |i, line| i + 1 } }
Like the above proc, which counts lines in a file.
count = open("/var/log/messages") | wc
Now, if you want to keep going and get these procs all chained together into a nice series of pipes and ongoing IO, see:
wc = proc { |io| IO[io.inject(0) { |i, line| i + 1 }] } grep = proc { |exp| proc { |io| IO[io.grep(exp)] } } count = open("/var/log/messages") | grep[/root/] | wc p count.read
Which can be done in a myriad of fashions, but I chose to use StringIO
in my script: ruby-pipe.rb. What fun. See Symbol.to_proc for a like kind of wonderment.
MenTaLguY
Have you been using Haskell on the quiet? Because, I swear, it looks like you have.
You are writing monadic combinators in Ruby, I mean! And currying lambdas!
Sneaky of you, calling your lambdas “procs”, but don’t think we wouldn’t notice…
MenTaLguY
(after checking out the
Symbol#to_proc
hack)Ah, goes to show—inside every imperative language there’s a plain functional language screaming to get out.
names.map(&:upcase)
is the sort of thing you take for granted in functional-land.Functional languages FLIP OUT and curry functions ALL THE TIME , man. It’s the REAL ULTIMATE POWER .
It’d be interesting to collect all these “functional-style” Ruby hacks in one place…
Danno
An internet cookie to the man that brings me Prolog’s head on a Ruby fox’s tail!
What’s really nice though, about all this functionalityness in Ruby, is that the syntax for using it is as good, if not better than pure functional languages!
It’s just too bad that the result has to be a StringIO object. Not sure that feels natural to me…
why
MenTaLguY: That’s exactly what I was thinking. The whole wrapper syntax is awesome for explaining monads, but too crufty to really use when loafing around the house. But this could work…
Danno: Yeah, it does feel odd. I guess do a String pipe.
cuz
Stuff like this might actually get the FP guys’ attention away from Python and onto Ruby. That’d be a group of smart programmers who could make some nice contributions to the Ruby community…
cuz
Makes the shell scripter in me happy too :O)
Danno
My first instinct is to just have grep and wc extend the object they return with the Piping module.
There seems to be a problem with extending an instance of a Fixnum though.
I’m asking around on #ruby-lang for a solution.
murphy
I really love all those neat syntax tricks, especially Symbol#to_proc, which would be great for Ruby 2.
But this example – I don’t know if it really proves that such hacks are usefull. Ruby has ”.” for ”|”; compare the above to the equivalent:
which is much more readable for Ruby folks.
Someone started creating a shell using Ruby syntax (don’t remember the name) – this would be the reverse idea…
As often, a really nice hack may be cool, but not really “useful” for real problems. Anyway, continue! This is the Fun of Ruby, so it is “useful” in a much deeper sense.
btw: what is this?
Is this Ruby 1.9?
@FP: I think Ruby has one real disadvantage over Haskell ‘n friends: the dynamic function definition (if you give less parameters than the function needs, it returns a function) is not implementable in Ruby. At least I don’t think it is possible with pure Ruby and with acceptable performance.
Whoever proves the opposite is my hero :D
Danno
Alrighty, here’s what me ‘n’ the boys on #ruby-lang came up with.
Solution to singleton-classes is to just extend the class itself.
I really like the Piping[] method, sorta like “Piping hot bowl of awesome comin’ right up!”murph: 1) IO[] was a method that why added to the IO class.
2) I’m tempted to take that challenge. Just one question: How do Haskell ‘n Friends handle the situation when there’s more than one method you could return a curried version of?
Specifically, I’m trying to think of how to handle optional parameters. And I’m relatively sure that in functional languages, you can have functions with differing numbers of arguments.
flgr
It’s great to see so many people like Symbol#to_proc, but _why, did you really have to use += instead of + in that inject block?
I fear more and more people are not understanding what inject actually does and what it should be used for. Don’t modify in the block or you can just as well use each—just go from an existing state to a new one and let inject do the work for you.
why
flgr: Missed that, thank you.
murphy: Yeah, true.
mental: After some thought… please forgive the word crufty. I’ve been up all day.
<|:{
Bravo! The ruby shell folks should incorporate this immediately.
chris2
Hmm, I did something like that in 2004, Object-oriented Pipelines.
Dave Burt
Murphy said: “I think Ruby has one real disadvantage over Haskell ‘n friends: the dynamic function definition (if you give less parameters than the function needs, it returns a function) is not implementable in Ruby.”
You’ll find even more interesting Ruby-currying in the ruby-talk archives.
MenTaLguY
why: Well, the thing is, in Ruby, it is crufty-feeling. Goes against the grain. So I think crufty is a perfectly fitting word.
I’ve got to stick with
pass
andwrap
, though, for pedagogical reasons (at least to start with), because they are those what make monads monads.Jumping right into combinators as you’ve done is actually useful, but it’s a few stages removed from the basic monad concept.
Also, since Ruby is so laissez-faire about types, it’s hard to avoid convenient shortcuts that take you out of monad-land. Working on the Monads-in-Ruby series, I’m beginning to feel like using monads properly in Ruby requires a discipline that none of us have.
MenTaLguY
why: Oh, and for what it’s worth, even in Haskell, most interesting uses of monads mainly involve combinators.
>>=
andreturn
are the foundations for stringing things together, not the meat and potatoes.MenTaLguY
(hence the popularity of
liftM
and friends, which hoist everyday functions into combinators in a particular monad)olleolleolle
You folks are reading my little mind, like a picture-book. The current FP craze is getting to all of us.
I just read David Mertz’ Beginning Haskell tutorial. (Free signup req’d. Oh well.) And that curry-in-Haskell statement was there, too. (Like dejà-new – something I have seen before, recently.)
I sat down with coloring pens to add syntax highlighting and some markup to add a little pedagogy to the raw Haskell source. Sometimes, I had to color as little as one character, to isolate something meaningful. FP takes a deft hand.
Mental, why, Dave Burt, (and the tail of the list of commenters) thanks for the explanation-informercials on Haskell, Raskell et al.
Adam Sanderson
It’s funny, when you’re looking at something like this you also pick up little things, I didn’t realize a proc could be accessed with [].
Now I just have to think of why I’d want to use the monadic bits.
Curried Chicken
Dave Burt said:
curry = proc {|f| proc {|x| proc {|y| f[x, y] } } }
So how does one go about calling this? I’m thinking that it should take a proc as an argument and that proc should take an argument:
curry[proc{|f| puts f }]
but that didn’t do anything. So how exactly would I use the above curry proc?
BTW : What’s a good (readable) source of info on Functional Programming concepts (preferrably using Ruby for the examples:) ? Is there some sort of Poignant Guide to FP? Perhaps _why would like to write a Functionally Poignant Guide to Ruby?
why
Curried Chicken: If you taste as deeply as you are nested, then please stay for lunch, I insist.
Well, one use of a proc like the above would be as a kind of factory for procs:
Basically, you need to ask if a certain bit of data would be better expressed as a function (
f(x)
) rather than just the data (y
).Like how
4.years
should probably return a function so thatTime.now - 4.years
would work right.Dave Burt
Chicken,
Close. The input function needs to take 2 arguments.
That curry proc is kinda like Haskell’s curry function: it converts a function that takes 2 arguments into a function that takes one argument, returning a function that takes the second argument which finally returns the result. Example time.
Curried Chicken
Ah, thanks Dave and _why. Now I’m much less chicken of currying and but I have one beef :) what is the usefulness of of curry? I mean it’s nice and all that I don’t need as many arguments, but still.
I await the Poignant Guide to Functional Ruby. I’ve been wanting to learn FP for some time now, but everytime I start the OCaml tutorial I’m put off by the syntax.
MenTaLguY
I’m considering doing such a guide, actually. The Monads in Ruby thing is merely a dress rehearsal. But no promises yet.
As for currying, it’s a useful way of building little helper functions on the spot. It basically amounts to applying a function part-way, leaving the remainder of the arguments dangling for later. You do it all the time without realizing (more on that in a moment).
It is based on Haskell Curry’s (yeah.) insight that:
Means essentially the same thing as (for example):
This means you can (conceptually) break up a function’s arguments however you like. Apply a three-argument function to one argument? You get a two-argument function. Apply a three-argument function to two arguments, you get a one-argument function.
Since Ruby won’t curry for you automatically, you can do it explicitly with lambdas—say you want to curry two arguments into a three-argument function.
See? Two arguments curried in, one argument still dangling.
What use is this? Well, as I said you’ve probably done a lot of currying without realizing—many of the simple things people do with blocks amount to currying.
Look, we are currying:
or more explicitly:
The thing about Functional Programming is that you are already doing most of it.
Trejkaz
A shell in Ruby syntax? You might be thinking of Rush.
Dave Burt
My (less helpful) contribution is only a direct port of bits of Haskell’s Standard Prelude. And it’s not useful in Ruby, except as food for thought. This is mostly because Ruby is not a strictly functional language, nor is it typed as tightly as Haskell, and Ruby offers its own ways to do what you’d use curry for in Haskell. Like Mental says: you’re already doing functional programming when you use Ruby’s blocks; Ruby’s procs are first-class functions. Looking at features of functional languages can provide new ways of looking at Ruby.
kig
rue
rush pipes will work a bit differently though they actually do override |, simply because the domain is a bit different.
Comments are closed for this entry.