Functional with Ruby

Hello list,

here a really simple Haskell function, which sums all elements of a list
(from the book "The Haskell School of Expression"):

···

--------------------------------
listSum [] = 0
listSum (x : xs) = x + listSum xs
--------------------------------

Which is the best way to say it in Ruby (functional style)? Don't use
'enum.inject', please!

Thanks for your thoughts.
Greetings,
Andreas Semt

In article <8759.1129747265@www88.gmx.net>,

···

Andreas Semt <Andreas.Semt@gmx.net> wrote:

Hello list,

here a really simple Haskell function, which sums all elements of a list
(from the book "The Haskell School of Expression"):
--------------------------------
listSum = 0
listSum (x : xs) = x + listSum xs
--------------------------------

Which is the best way to say it in Ruby (functional style)? Don't use
'enum.inject', please!

Ummm, why wouldn't we be able to use inject? I was under the impression that
inject was the functional way to do it in Ruby.

Phil

What's wrong with inject? I find this very readable and not any more
verbose than the Haskell:

irb(main):001:0> a=[1,2,3,4,5,6,7]
=> [1, 2, 3, 4, 5, 6, 7]
irb(main):002:0> a.inject(0){|i,sum| i+sum}
=> 28
irb(main):003:0> 1+2+3+4+5+6+7
=> 28
irb(main):004:0> module Enumerable
irb(main):005:1> def list_sum
irb(main):006:2> self.inject(0){|i,sum| i+sum}
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):009:0> a.list_sum
=> 28

Ryan

···

On 10/19/05, Andreas Semt <Andreas.Semt@gmx.net> wrote:

Hello list,

here a really simple Haskell function, which sums all elements of a list
(from the book "The Haskell School of Expression"):
--------------------------------
listSum = 0
listSum (x : xs) = x + listSum xs
--------------------------------

Which is the best way to say it in Ruby (functional style)? Don't use
'enum.inject', please!

But inject *is* functional! It's equivalent to Lisp's accumulate. It
doesn't modify the list at all or have any side-effects.

Perhaps you're asking for a function that takes the list as an
argument, rather than a method that operates on the lisp. For
example:

def listsum(x)
  x.size == 0 ? 0 : x[0] + listsum(x[1..-1])
end

This looks more like the Haskell, but it's not idiomatic for Ruby. It
only looks like a standalone function, it's really a method on Object.
Ruby can be used this way, but it's going against the grain.

The Ruby way is definitely x.inject {|a,b| a + b}, or if you'll be
using it a lot:

class Array
  def sum
    inject {|a,b| a+b}
  end
end

regards,
Ed

···

On Thu, Oct 20, 2005 at 03:41:09AM +0900, Andreas Semt wrote:

here a really simple Haskell function, which sums all elements of a list
(from the book "The Haskell School of Expression"):
--------------------------------
listSum = 0
listSum (x : xs) = x + listSum xs
--------------------------------

Which is the best way to say it in Ruby (functional style)? Don't use
'enum.inject', please!

Andreas Semt wrote:

here a really simple Haskell function, which sums all elements of a list
(from the book "The Haskell School of Expression"):
--------------------------------
listSum = 0
listSum (x : xs) = x + listSum xs
--------------------------------

Which is the best way to say it in Ruby (functional style)? Don't use
'enum.inject', please!

You can abuse Ruby arrays for lists:

def list_sum(l)
  case l
  when then 0
  else l.first + list_sum(l[1, l.size])
  end
end
list_sum([1,2,3]) # => 6

But using Array#slice for getting the "tail" of the "list" could be inefficient.

You could also use my lazylist gem and the do this:

require 'lazylist'
def list_sum(l)
  case l
  when LazyList::Empty then 0
  else l.head + list_sum(l.tail)
  end
end
list_sum list(1,2,3)

That way you can also create an infinite ones lazy list in Ruby with this nice notation:
ones = list(1) { ones } # => [1,... ]

···

--
Florian Frank

harp:~ > cat a.rb
list_sum = lambda{|*list| list.flatten!; list.empty? ? 0 : list.shift + list_sum[list] }

p list_sum[ 0b101000, 0b10 ]

harp:~ > ruby a.rb
42

-a

···

On Thu, 20 Oct 2005, Andreas Semt wrote:

Hello list,

here a really simple Haskell function, which sums all elements of a list
(from the book "The Haskell School of Expression"):
--------------------------------
listSum = 0
listSum (x : xs) = x + listSum xs
--------------------------------

Which is the best way to say it in Ruby (functional style)? Don't use
'enum.inject', please!

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
anything that contradicts experience and logic should be abandoned.
-- h.h. the 14th dalai lama

===============================================================================

I'm not going to answer your concrete question. I'm going to answer the question you should be asking....

The answer is with the exception of the the non '!' string methods and numbers most of ruby operations are non-"functional pure" in the sense that they have side effects and change state.

Thus to recapture "functional" programming purity in Ruby, for most objects and operations on them requires you to make very heavy use of the clone method. ie. Force yourself to go obj.clone.operation to make ruby into something like the "Self" language.

Certainly in some contexts (eg. when you can prove there are no other references to the object in question) a clone + modify is equivalent to just a modify. Thus you can make an optimization in such cases, but you will receive very little support from the language in proving this.

It doesn't even have C++'s "const" keyword. Although it does have "freeze", which unfortunately only works at run time after the object has been frozen, which tends to be a bit late.

On the other hand ruby is a "functional" language in the sense that procs, blocks and methods, etc are all first order objects and you can curry dice and slice them. It even has several flavours of eval.

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

Carter's Clarification of Murphy's Law.

"Things only ever go right so that they may go more spectacularly wrong later."

From this principle, all of life and physics may be deduced.

···

On Thu, 20 Oct 2005, Andreas Semt wrote:

Which is the best way to say it in Ruby (functional style)?

Andreas Semt wrote:

Hello list,

here a really simple Haskell function, which sums all elements of a list
(from the book "The Haskell School of Expression"):
--------------------------------
listSum = 0
listSum (x : xs) = x + listSum xs
--------------------------------

Which is the best way to say it in Ruby (functional style)? Don't use
'enum.inject', please!

Thanks for your thoughts.
Greetings,
Andreas Semt

irb(main):011:0> def sum(a,i)
irb(main):012:1> i<a.size ? a[i] + sum(a,i.succ) : 0
irb(main):013:1> end
=> nil
irb(main):014:0> sum([1,3,5,7,9], 0)
=> 25

Hello list,

so many thanks for all your answers!
My original interest in this question was learning functional
programming, with both languages.
Ruby is my language of choice, so i try to compare both languages to
find out how much functional ruby can be. All the proposals are really
helpfull, both for understandig functional programming better *and* for
understanding the principles of ruby.

  This list is just great ...

Best regards,
Andreas Semt

One small thing, I had the order wrong on the parameters (i.e. the
names are misleading.) It should be:

a.inject(0){|sum,i| sum+i}

It still works either way in this case. But other kinds of injects may
not be commutative, so I just wanted to make sure I didn't mess up any
newbies :slight_smile:

ri is your friend.

Ryan

···

On 10/19/05, Ryan Leavengood <leavengood@gmail.com> wrote:

irb(main):002:0> a.inject(0){|i,sum| i+sum}

But inject *is* functional! It's equivalent to Lisp's accumulate. It
doesn't modify the list at all or have any side-effects.

Exactly.

Incidentally, the original example isn't really the best way to do this in
Haskell either. I haven't written any Haskell for a while, so some of this
might not be syntactically correct, but you get the idea. Please correct me
if any of this is wrong.

The above would best be expressed something like this (Iike I said, it's
been a while, so the parameter order might be wrong):

foldr + 0 list

foldr does exactly the same thing as inject. It takes a binary function, a
starting value, and a list. The neat thing about Haskell is that the
addition operator--actually any function--is automatically curried.

I'd make the claim here that the use of the higher order function is
actually better functional style than the explicit recursion.

If I recall correctly, many of the list functions in the Hugs standard
library are actually implemented this manner.

That can get a bit shorter too, since you don't need the (0) part.

James Edward Gray II

···

On Oct 19, 2005, at 1:59 PM, Ryan Leavengood wrote:

What's wrong with inject? I find this very readable and not any more
verbose than the Haskell:

irb(main):001:0> a=[1,2,3,4,5,6,7]
=> [1, 2, 3, 4, 5, 6, 7]
irb(main):002:0> a.inject(0){|i,sum| i+sum}
=> 28

"Ara.T.Howard" <Ara.T.Howard@noaa.gov> wrote in message
news:Pine.LNX.4.62.0510191259580.28380@harp.ngdc.noaa.gov...

···

On Thu, 20 Oct 2005, Andreas Semt wrote:

Hello list,

here a really simple Haskell function, which sums all elements of a list
(from the book "The Haskell School of Expression"):
--------------------------------
listSum = 0
listSum (x : xs) = x + listSum xs
--------------------------------

Which is the best way to say it in Ruby (functional style)? Don't use
'enum.inject', please!

harp:~ > cat a.rb
list_sum = lambda{|*list| list.flatten!; list.empty? ? 0 : list.shift +
list_sum[list] }

p list_sum[ 0b101000, 0b10 ]

harp:~ > ruby a.rb
42

OK, this is the first actually functional post -- list_sum is a first-class
function.

Here's my translation:

list_sum = Proc.new {|x, *xs| if x then x + list_sum[*xs] else 0 end }

Cheers,
Dave

Hi --

Andreas Semt wrote:

here a really simple Haskell function, which sums all elements of a list
(from the book "The Haskell School of Expression"):
--------------------------------
listSum = 0
listSum (x : xs) = x + listSum xs
--------------------------------

Which is the best way to say it in Ruby (functional style)? Don't use
'enum.inject', please!

You can abuse Ruby arrays for lists:

def list_sum(l)
case l
when then 0
else l.first + list_sum(l[1, l.size])
end
list_sum([1,2,3]) # => 6

Just for fun I was trying to see how visually close I could get to the
Haskell. The answer is... not very :slight_smile: But the star operator
actually handles the x : xs list thing rather well:

   def list_sum(x,*xs)
     case xs
       when : x # note: not 0 when done this way
       else x + list_sum(*xs)
     end
   end

David

···

On Thu, 20 Oct 2005, Florian Frank wrote:

--
David A. Black
dblack@wobblini.net

i don't think it is functional because the name of the function cannot be used
as a first class object. eg you cannot to

   the_function = the_other_function

without calling 'the_other_function' __unless__ the objects in question are
first class objects. in ruby you must use lambda to acheive something close
to this. at least i think this right...

-a

···

On Thu, 20 Oct 2005, Louis J Scoras wrote:

But inject *is* functional! It's equivalent to Lisp's accumulate. It
doesn't modify the list at all or have any side-effects.

Exactly.

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
anything that contradicts experience and logic should be abandoned.
-- h.h. the 14th dalai lama

===============================================================================

Louis J Scoras wrote:

The above would best be expressed something like this (Iike I said, it's
been a while, so the parameter order might be wrong):

foldr + 0 list

Well, foldr is defined recursively in Prelude.hs:

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f z = z
foldr f z (x:xs) = f x (foldr f z xs)

BTW, it's

foldr (+) 0 list

···

--
Florian Frank

James Edward Gray II wrote:

That can get a bit shorter too, since you don't need the (0) part.

Yeah. And I always hated it, that your program is going to crash, if "a" happens to be empty and you naively
use the inject result for further processing.

···

--
Florian Frank

Yeah I figured that out after posting, hehe (at the same time ri told
me my parameter ordering was wrong.) Inject is such a cool method.

Ryan

···

On 10/19/05, James Edward Gray II <james@grayproductions.net> wrote:

That can get a bit shorter too, since you don't need the (0) part.

nice. still the functional vs. non is a very thin line in ruby:

   harp:~ > cat a.rb
   class << self
     def list_sum x = nil, *xs
       x ? x + list_sum(*xs) : 0
     end
   end

   list_sum = method 'list_sum'

   p list_sum[0b101000, 0b10]
   p list_sum[42]
   p list_sum

   harp:~ > ruby a.rb
   42
   0

ps. i'm trying to wrap my mind around ocaml attm.

regards.

-a

···

On Thu, 20 Oct 2005, Dave Burt wrote:

"Ara.T.Howard" <Ara.T.Howard@noaa.gov> wrote in message
news:Pine.LNX.4.62.0510191259580.28380@harp.ngdc.noaa.gov...

On Thu, 20 Oct 2005, Andreas Semt wrote:

Hello list,

here a really simple Haskell function, which sums all elements of a list
(from the book "The Haskell School of Expression"):
--------------------------------
listSum = 0
listSum (x : xs) = x + listSum xs
--------------------------------

Which is the best way to say it in Ruby (functional style)? Don't use
'enum.inject', please!

harp:~ > cat a.rb
list_sum = lambda{|*list| list.flatten!; list.empty? ? 0 : list.shift +
list_sum[list] }

p list_sum[ 0b101000, 0b10 ]

harp:~ > ruby a.rb
42

OK, this is the first actually functional post -- list_sum is a first-class
function.

Here's my translation:

list_sum = Proc.new {|x, *xs| if x then x + list_sum[*xs] else 0 end }

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
anything that contradicts experience and logic should be abandoned.
-- h.h. the 14th dalai lama

===============================================================================

Well, that's true of Ruby in general. If you want to turn a method
name into a first class object, you can do this:

x = myarray.instance_method(:inject)
x.call {|a,b| a+b}

Or this:

x = Array.instance_method(:inject)
x.bind(myarray).call {|a,b| a+b}

regards,
Ed

···

On Thu, Oct 20, 2005 at 04:34:49AM +0900, Ara.T.Howard wrote:

i don't think it is functional because the name of the function
cannot be used as a first class object.