Point free (pointless) programming in ruby? (fwd)

View: New views
6 Messages — Rating Filter:   Alert me  

Point free (pointless) programming in ruby? (fwd)

by JohnCarter :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Here's a post I sent to the ruby mailing list about doing pointfree /
concatenative like things in Ruby.

Perhaps some of the Denizens of this list might like to apply their
wondrously creative minds to the exercise.



John Carter                             Phone : (64)(3) 358 6639
Tait Electronics                        Fax   : (64)(3) 359 4632
PO Box 1645 Christchurch                Email : john.carter@...
New Zealand


---------- Forwarded message ----------
Date: Wed, 14 Jan 2009 12:12:00 +1300 (NZDT)
From: John Carter <john.carter@...>
To: ruby-talk ML <ruby-talk@...>
Subject: Point free (pointless) programming in ruby?

I'm very fond of the notion of Concatenative Languages such as Joy,
Factor...
   http://en.wikipedia.org/wiki/Joy_(programming_language)
   http://www.latrobe.edu.au/philosophy/phimvt/joy/jp-joyjoy.html
   http://factorcode.org/

There is a supreme simplicity about them.

So while reading what the current state of them is, I stumbled across
the notion of Point free (sometimes called Pointless) programming.

The idea of sequences of function compositions that elide the
arguments that they will be applied to.

http://www.haskell.org/haskellwiki/Pointfree

One can easily define a sequence of methods in Ruby...

fun_seq =  [:a, :b, :c]

as a sequence of symbols.

Which one could apply to an arbitrary argument..

irb
> f = [:to_s, :succ]
=> [:to_s, :succ]
> f.inject(4,:send)
=> "5"

Oooh! Looky! That's sneaky of me!
   symbol_sequence.inject(arg,:send)

   [:a, :b, :c].inject(arg,:send)
that's equivalent to
   arg.a.b.c

Not quite function composition. Cute, but let's try...

irb
> def a(a)
>  p ["a",a]
> a+"a"
> end
=> nil
> def b(b)
>  p ["b",b]
> b+"b"
> end
=> nil
>  def c(c)
> p ["c",c]
> c+"c"
> end
=> nil
> f=[:a,:b,:c]
=> [:a, :b, :c]
> f.inject("foo"){|memo,obj|send(obj,memo)}
["a", "foo"]
["b", "fooa"]
["c", "fooab"]
=> "fooabc"

[:a, :b, :c].inject(arg){|memo,obj|send(obj,memo)}
  is equivalent to
c(b(a(arg)))

> c(b(a("bah")))
["a", "bah"]
["b", "baha"]
["c", "bahab"]
=> "bahabc"

Or how about working with lambda's or Proc objects...

> a1 = lambda {|x| x+"a"}
=> #<Proc:0xb7d9f744@(irb):41>
> b1 = lambda {|x| x+"b"}
=> #<Proc:0xb7e0feb8@(irb):42>
> c1 = lambda {|x| x+"c"}
=> #<Proc:0xb7dfa5e0@(irb):43>
> [a1, b1, c1]
=> [#<Proc:0xb7d9f744@(irb):41>, #<Proc:0xb7e0feb8@(irb):42>,
#<Proc:0xb7dfa5e0@(irb):43>]
> [a1, b1, c1].inject("foo"){|memo,proc| proc.call(memo)}
=> "fooabc"


Anyhoo! Clearly in principle one can do Pointfree programming Ruby.

Questions for the Group:

1) Is there a neater way of expressing a sequence of function
    compositions in Ruby?

2) Which Ruby Pointfree sequences are actually useful?



John Carter                             Phone : (64)(3) 358 6639
Tait Electronics                        Fax   : (64)(3) 359 4632
PO Box 1645 Christchurch                Email : john.carter@...
New Zealand


Re: Point free (pointless) programming in ruby? (fwd)

by John Nowak :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Jan 13, 2009, at 6:15 PM, John Carter wrote:

> 1) Is there a neater way of expressing a sequence of function
>    compositions in Ruby?

Evaluation of a concatenative language is just a fold of 'apply' over  
your list of functions with the empty list as the initial element. For  
example, in Scheme:

    (define (ceval fs) (fold apply '() fs))

Your Ruby version is essentially the same thing.

> 2) Which Ruby Pointfree sequences are actually useful?

All you can really do is implement a concatenative language inside  
Ruby complete with its own standard library. This is probably not so  
useful.

Languages like ML and Haskell work better because they have curried  
functions. There even exists a tool for automatically translating  
pointful Haskell functions to point-free Haskell functions, although  
the results are often less than clear:

    \x y z -> y * z - x  ==  flip (flip . ((-) .) . (*))

You may want to look at the following:
http://www.haskell.org/haskellwiki/Pointfree

Keep in mind that all existing concatenative languages make heavy use  
of stacks. Related languages like FP and FL make heavy use of lists.  
Trying to do pointfree programming in languages that don't do such  
things (Ruby, Python, Haskell, Scheme, etc) is going to be quite  
painful. For example, here's the Haskell example above in Joy:

    * swap -

And in a concatenative FP-style language:

    - [hd, * tl]

- John

Re: Point free (pointless) programming in ruby? (fwd)

by JohnCarter :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Tue, 13 Jan 2009, John Nowak wrote:

> Languages like ML and Haskell work better because they have curried
> functions.

I wonder if Curried Functions have the same effect as Curried Eggs
have on the digestive system?

Actually Currying works quite well in Ruby...

two_arg = lambda{|a,b| somefunc(a,b)}

b="Some concrete value"
one_arg = lambda{|a| two_arg.call(a,b)}

one_arg.call("Single parameter") is equivalent to
someFunc( "Single Parameter", "Some concrete value")

> Keep in mind that all existing concatenative languages make heavy use
> of stacks. Related languages like FP and FL make heavy use of lists.
> Trying to do pointfree programming in languages that don't do such
> things (Ruby, Python, Haskell, Scheme, etc) is going to be quite
> painful. For example, here's the Haskell example above in Joy:

Well, actually Ruby has Array's that behave (or can be made to behave)
in almost all respects like lists.

The obj.a.b.c.d.e form would be effectively identical to the
concantenative stack to stack functions if obj was an Array and each
method a, b, c, d, e was a non-destructive Array method returning an
Array.

Well, actually Rubies Duck Typing extends that in interesting
ways. See the Ruby Enumerable mixin for an example.

ie. Ruby is a superset of concatenative languages.

ie. If one restricts ones activities to a suitable concatenative
subset, the same interesting properties emerge.

The question then remains... What interesting idioms could one evolve
by playing in that subset.



John Carter                             Phone : (64)(3) 358 6639
Tait Electronics                        Fax   : (64)(3) 359 4632
PO Box 1645 Christchurch                Email : john.carter@...
New Zealand


Re: Point free (pointless) programming in ruby? (fwd)

by John Nowak :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Jan 15, 2009, at 5:37 PM, John Carter wrote:

> On Tue, 13 Jan 2009, John Nowak wrote:
>
>> Languages like ML and Haskell work better because they have curried
>> functions.
>
> Actually Currying works quite well in Ruby...
>
> two_arg = lambda{|a,b| somefunc(a,b)}
>
> b="Some concrete value"
> one_arg = lambda{|a| two_arg.call(a,b)}

That is not currying. Unfortunately, this is something of a common  
misunderstanding. A curried function is one that emulates taking more  
than one argument by using many functions that take a single argument.  
For example, here's a function in Haskell that adds three numbers:

    foo = \x y z -> x + y + z

And here's *the exact same function* with a different name:

    bar = \x -> (\y -> (\z -> x + y + z))

When you write the former, you're really doing the later. Both can be  
called identically as they're exactly the same thing:

    foo (1, 2, 3)  -- 6
    bar (1, 2, 3)  -- 6

Because 'foo' and 'bar' are curried, we can do this to mean the *exact  
same thing* once again; this is called partial application:

    ((foo 1) 2) 3  -- 6
    ((bar 1) 2) 3  -- 6

Additionally, Haskell offers a function for currying a function:

    curry (\(x, y) -> x + y)  ==  \x y -> x + y

And uncurrying:

    uncurry (\x y -> x + y)  ==  \(x, y) -> x + y

Now, you *can* write a curried function in Ruby like so:

    baz = lambda{|a| lambda{|b| lambda{|c| a + b + c}}}

And call it as such:

    baz.call(1).call(2).call(3)  # 6

Unfortunately, it's quite painful.

I'm not sure where the confusion with respect to "curry" started.  
Factor is an example of a language that unfortunately misuses the term.

>> Keep in mind that all existing concatenative languages make heavy use
>> of stacks. Related languages like FP and FL make heavy use of lists.
>> Trying to do pointfree programming in languages that don't do such
>> things (Ruby, Python, Haskell, Scheme, etc) is going to be quite
>> painful. For example, here's the Haskell example above in Joy:
>
> Well, actually Ruby has Array's that behave (or can be made to behave)
> in almost all respects like lists.

Yes, you could write all new functions that operate on stacks instead  
of using normal parameters. Ruby performance being what it is though,  
I can't imagine this being useful for much of anything. Languages like  
Factor do analysis to eliminate constant reading from and writing to a  
stack which improves performance dramatically. Having to update an  
array's contents, make bounds checks, update an array's length, and so  
on just to add two numbers doesn't sound like fun.

> ie. Ruby is a superset of concatenative languages.
> ie. If one restricts ones activities to a suitable concatenative
> subset, the same interesting properties emerge.

Yes, if you rewrite every function to work on stacks and use none of  
Ruby's features, you will have a very poor concatenative language with  
awful syntax. Of course, one can implement Ruby's features in a  
concatenative language too. It's therefore a bit silly to claim one is  
a superset of the other.

- John

Re: Point free (pointless) programming in ruby? (fwd)

by JohnCarter :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Thu, 15 Jan 2009, John Nowak wrote:

> That is not currying. Unfortunately, this is something of a common
> misunderstanding.

You are right, sorry.

> Now, you *can* write a curried function in Ruby like so:
>
>    baz = lambda{|a| lambda{|b| lambda{|c| a + b + c}}}
>
> And call it as such:
>
>    baz.call(1).call(2).call(3)  # 6
>
> Unfortunately, it's quite painful.

Try an alternate syntax then...
irb
irb(main):001:0>  baz = lambda{|a| lambda{|b| lambda{|c| a + b + c}}}
=> #<Proc:0xb7d94ab0@(irb):1>
irb(main):002:0> baz[1]
=> #<Proc:0xb7d94ba0@(irb):1>
irb(main):003:0> baz[1][2]
=> #<Proc:0xb7d94c40@(irb):1>
irb(main):004:0> baz[1][2][3]
=> 6

> It's therefore a bit silly to claim one is a superset of the other.

Perhaps a better way of putting it would given the very flexible
multi-paradigm nature of Ruby, one can borrow the "Pointfree"
paradigm as well.

Will it be useful? Well, I suspect it might be.

I haven't seen anything yet that has given me the urge to rip Ruby out
as my main day to day programming workhorse. However there are parts
of the concatenative languages that make feel "ooh that has so much
promise... but not quite something I can convince my boss about".

Ruby doesn't quite comfortably replace the *sh languages yet.

One area that has been a bit hard to express is the *sh '|' pipelines.

I'm playing with the idea of pointfree in ruby as a nice paradigm to
do pipelined oneliners and more.



John Carter                             Phone : (64)(3) 358 6639
Tait Electronics                        Fax   : (64)(3) 359 4632
PO Box 1645 Christchurch                Email : john.carter@...
New Zealand


Re: Point free (pointless) programming in ruby? (fwd)

by John Nowak :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Jan 15, 2009, at 8:49 PM, John Carter wrote:

> I haven't seen anything yet that has given me the urge to rip Ruby out
> as my main day to day programming workhorse.

 >> x = 0
=> 0
 >> x
=> 0
 >> lambda{|x| 0}.call(5)
=> 0
 >> x
=> 5

That gave me the urge to do something alright.

Yes, I realize it has been fixed. Unfortunately it took 12 years,  
which can only mean that those involved have no clue what they're  
doing. But now I'm just being rude...

- John