hoodwink.d enhanced
RSS
2.0
XHTML
1.0

RedHanded

Hopping Through Pipes and Closures #

by why in inspect

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.

said on 19 Nov 2005 at 14:22

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…

said on 19 Nov 2005 at 14:57

(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…

said on 19 Nov 2005 at 17:21

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…

said on 19 Nov 2005 at 17:40

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.

said on 19 Nov 2005 at 19:03

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…

said on 19 Nov 2005 at 19:04

Makes the shell scripter in me happy too :O)

said on 19 Nov 2005 at 19:31

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.

said on 19 Nov 2005 at 19:39

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:

p open("/var/log/messages").grep(/root/).size

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?

IO[...]

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

said on 19 Nov 2005 at 20:00

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!”

module Piping
   def | proc 
        proc.call self
    end

    def self.[](obj)
        if obj.class.respond_to? :new
            obj.extend(Piping)
        else
       obj.class.extend(Piping)
        end
        obj
    end
end

class IO
  include Piping
end

wc = proc { |io| Piping[io.inject(0) { |i, line| i += 1 }] }
grep = proc { |exp| proc { |io| Piping[io.grep(exp)] } }

count = open("ruby-pipe.rb") | grep[/io/] | wc
p count

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.

said on 19 Nov 2005 at 20:23

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.

said on 19 Nov 2005 at 21:34

flgr: Missed that, thank you.

murphy: Yeah, true.

mental: After some thought… please forgive the word crufty. I’ve been up all day.

said on 20 Nov 2005 at 00:14

Bravo! The ruby shell folks should incorporate this immediately.

said on 20 Nov 2005 at 05:42

Hmm, I did something like that in 2004, Object-oriented Pipelines.

said on 20 Nov 2005 at 09:46

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.”


# curry :: ((a, b) -> c) -> a -> b -> c
curry = proc {|f| proc {|x| proc {|y| f[x, y] } } }

You’ll find even more interesting Ruby-currying in the ruby-talk archives.

said on 20 Nov 2005 at 12:05

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 and wrap, 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.

said on 20 Nov 2005 at 12:08

why: Oh, and for what it’s worth, even in Haskell, most interesting uses of monads mainly involve combinators. >>= and return are the foundations for stringing things together, not the meat and potatoes.

said on 20 Nov 2005 at 12:09

(hence the popularity of liftM and friends, which hoist everyday functions into combinators in a particular monad)

said on 20 Nov 2005 at 17:27

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.

said on 21 Nov 2005 at 11:07

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.

said on 22 Nov 2005 at 13:18

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?

said on 22 Nov 2005 at 14:40

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:

 oper = proc do |op|
   proc do |x, y|
     x.method(op).call(y)
   end
 end

 add = oper[:+]
 puts add[1,2]  #=> 3

 sub = oper[:-]
 puts sub[2,1]  #=> 1

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 that Time.now - 4.years would work right.

said on 22 Nov 2005 at 18:07

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.


add = proc {|x, y| x + y }
add[1, 2] #=> 3
curried_add = curry[add]
add_three = curried_add[3]
add_three[4] #=> 7
said on 22 Nov 2005 at 18:32

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.

said on 22 Nov 2005 at 19:34

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:

lambda { |a, b, c| ... }

Means essentially the same thing as (for example):

lambda { |a| lambda { |b| lambda { |c| ... } } }

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.

lambda { |c| zoog( 1, 2, c ) }

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:


blahs.map do |c|
  zoog( 1, 2, c )
end

or more explicitly:


blahs.map &lambda { |c| zoog( 1, 2, c ) }

The thing about Functional Programming is that you are already doing most of it.

said on 22 Nov 2005 at 19:50

A shell in Ruby syntax? You might be thinking of Rush.

said on 23 Nov 2005 at 08:36

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.

said on 23 Nov 2005 at 13:17

class Fun < Proc
  def call(*args)
    if args.size < arity.abs and arity != -1
      Fun.new{|*a| call(*(args+a)) }
    else
      super
    end
  end
  alias_method :[], :call
end

def fun &block
  Fun.new &block
end

f = fun{|a, b, c| a * b / c }

f[10, 20, 30] == f[10][20][30]
#=> true
said on 27 Nov 2005 at 23:50

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.