Forum: Ruby functional programming

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
1b30afec8cd3efa4c7a10980c10315d8?d=identicon&s=25 Haris Bogdanovic (Guest)
on 2009-01-08 02:55
(Received via mailing list)
Hi.

I'm starting to learn functional programming with Ruby.
I would like for someone to write me an example of finding the smallest
number in the list and a sorting algorithm.
I got the concept of map (collect), filter (select, detect and reject)
and
reduce (inject) higher order functions but I don't know how to compose
algorithms from them.
So please write me those two algorithms or some other simple ones with
some
explanation.
Some links to this subject would be nice also.

Thanks
Haris
Aafa8848c4b764f080b1b31a51eab73d?d=identicon&s=25 Phlip (Guest)
on 2009-01-08 06:15
(Received via mailing list)
> I'm starting to learn functional programming with Ruby.

Wouldn't learning OO programming in Haskel be easier?
1b30afec8cd3efa4c7a10980c10315d8?d=identicon&s=25 Haris Bogdanovic (Guest)
on 2009-01-08 11:27
(Received via mailing list)
I know Ruby programming well but I wanted to add funtional paradigm in
my
programming as it is very usefull.
If you don't know and are not interested in it you don't have to insult
me.
And Haskell looks pretty useless to me because it is functional paradigm
only language.

"Phlip" <phlip2005@gmail.com> wrote in message
news:rSf9l.8378$8_3.226@flpi147.ffdc.sbc.com...
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-08 12:22
Haris Bogdanovic wrote:
> I know Ruby programming well but I wanted to add funtional paradigm in
> my
> programming as it is very usefull.

If you know Ruby well - and you obviously understand functional
programming, because you know it is "usefull" (sic) - then just start
writing functional programs. To do this, make sure that:

1. you don't modify any objects. Only use methods which return new
objects.

2. once a local variable is assigned, you don't reassign it.

However Ruby won't enforce (1) unless you litter your code with 'freeze'
statements, and it can't enforce (2) at all.

a = "hello"
a << " world"     # wrong: modifies the string
a += " world"     # wrong: new string, but assigned to same variable
b = a + " world"  # right

# Example: convert all hash keys to strings
src = {:one=>1, :two=>2}

# Imperative
out = src.inject({}) { |h,(k,v)| h[k.to_s] = v; h }

# Functional
out = src.inject({}) { |h,(k,v)| h.merge(k.to_s => v) }

> If you don't know and are not interested in it you don't have to insult
> me.
> And Haskell looks pretty useless to me because it is functional paradigm
> only language.

Who's being insulting now? What are you saying about all Haskell
programmers, and all the language designers? Do you genuinely believe
that useful systems cannot be written in Haskell?

Go and look at Erlang. Massive real-world systems are built out of this,
with incredible reliability, using only functional programming and CSP.
It's also very straightforward to pick up and use, and has a very
comprehensive standard library.

Remember also that functional languages allow all sorts of compile-time
optimisations which are impossible in Ruby, so there is the potential
for much higher performance. For example, OCaml generates code which is
reputedly only half the speed of the equivalent C code.
2f4d4f9c35ea851bffb9a9cc2e086365?d=identicon&s=25 Harry Kakueki (Guest)
on 2009-01-08 16:13
(Received via mailing list)
Since you are using Ruby, here is one Ruby way;


list = [6,4,5,8,12,34,5,6,778,9,777,3,45]
p list.min #> 3
p list.sort #> [3, 4, 5, 5, 6, 6, 8, 9, 12, 34, 45, 777, 778]
3bb23e7770680ea44a2d79e6d10daaed?d=identicon&s=25 M. Edward (Ed) Borasky (Guest)
on 2009-01-08 16:31
(Received via mailing list)
Brian Candler wrote:
> objects.
>
> 2. once a local variable is assigned, you don't reassign it.
>
> However Ruby won't enforce (1) unless you litter your code with 'freeze'
> statements, and it can't enforce (2) at all.

Well then ... just as "it's possible to write FORTRAN programs in any
language", it's *possible* to write "functional" programs in any
language. But as you point out, a pure functional style "goes against
the grain" in Ruby, if it doesn't or can't enforce (or even diagnose)
"non-functional" code.

Programming is as much, or more, about communication among the
programmers and between the programmers and the users as it is about
communication between the programmers and the "machine." So if you have
to spend time saying, "this is functional code", you've already lost
something, and introduced opportunities for mis-translation.

In short, I suspect that a more practical solution might be a language
that has object orientation and functional coding styles both baked in
right from the start. I'm not an expert on all the dozens of "obscure"
languages out there, so I can't give any names. But I know they exist.

> Go and look at Erlang. Massive real-world systems are built out of this,
> with incredible reliability, using only functional programming and CSP.
> It's also very straightforward to pick up and use, and has a very
> comprehensive standard library.
>
> Remember also that functional languages allow all sorts of compile-time
> optimisations which are impossible in Ruby, so there is the potential
> for much higher performance. For example, OCaml generates code which is
> reputedly only half the speed of the equivalent C code.

Did you mean "twice the speed?" Is OCaml faster or slower than C? And
why?
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-08 17:11
(Received via mailing list)
"M. Edward (Ed) Borasky" <znmeb@cesmail.net> writes:
> In short, I suspect that a more practical solution might be a language
> that has object orientation and functional coding styles both baked in
> right from the start. I'm not an expert on all the dozens of "obscure"
> languages out there, so I can't give any names. But I know they exist.

Common Lisp.
1b30afec8cd3efa4c7a10980c10315d8?d=identicon&s=25 Haris Bogdanovic (Guest)
on 2009-01-08 18:25
(Received via mailing list)
Thank you guys for your answers. Ruby is my favourite language mostly
because it is pure object oriented and above that you can incorporate
functional programming which has it's advantages; smaller, clearer, less
error prone code.

"Haris Bogdanovic" <fbogdanovic@xnet.hr> wrote in message
news:gk3m77$1ft$1@gregory.bnet.hr...
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-08 23:00
M. Edward (Ed) Borasky wrote:
>> For example, OCaml generates code which is
>> reputedly only half the speed of the equivalent C code.
>
> Did you mean "twice the speed?" Is OCaml faster or slower than C? And
> why?

I did mean half the speed, i.e. OCaml is slower.

OCaml is a high-level language, but code written in OCaml still manages
to achieve ~50% of the speed of hand-written C. Given that C can be
considered portable machine code, this is a pretty impressive
achievement.
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-08 23:35
(Received via mailing list)
"Haris Bogdanovic" <fbogdanovic@xnet.hr> writes:
> I'm starting to learn functional programming with Ruby.
> I would like for someone to write me an example of finding the smallest
> number in the list and a sorting algorithm.
> I got the concept of map (collect), filter (select, detect and reject) and
> reduce (inject) higher order functions but I don't know how to compose
> algorithms from them.
> So please write me those two algorithms or some other simple ones with some
> explanation.

You would have first to define the concept of list.  Indeed, the other
answer may have you misled into believe that [1,2,3] was a list, but it
is not, it is an Array:

irb(main):477:0> [1,2,3].class
Array

(actually it's a vector, Ruby has no multidimentional arrays like
Fortran or Lisp, or any other serrious programming language).


So to make a list, first, the basic building block, the cons cell:

(class Cons
 (attr_accessor :car)
 (attr_accessor :cdr)
 (def initialize(car,cdr)
    (@car = car)
    (@cdr = cdr)
  end)
end)


Let's wrap this object into a functional abstraction layer:

(def cons(car,cdr)
 (Cons . new(car,cdr))
end)

(def car(x)
 (x . car)
end)

(def cdr(x)
 (x . cdr)
end)

(def null(x)
 (x . nil?)
end)

irb(main):040:0> (cons 1,2)
#<Cons:0x7fdfb53eb808 @cdr=2, @car=1>
irb(main):042:0> (car (cons 1,2))
1
irb(main):043:0> (cdr (cons 1,2))
2
irb(main):044:0> (null (cons 1,2))
false
irb(main):045:0> (null (car (cons 1,nil)))
false
irb(main):046:0> (null (cdr (cons 1,nil)))
true



Then you can build a list over these cons cells:

(def list(*args)
  (i = (args . length))
  (r = nil)
  (loop {
     (if (i == 0)
        (break)
      else
        (i = (i - 1))
        (r = (cons (args [ i ]),r))
     end)
  })
 r
end)

irb(main):127:0> (list 1,2,3)
#<Cons:0x7fdfb53b81d8 @cdr=#<Cons:0x7fdfb53b8200
@cdr=#<Cons:0x7fdfb53b8228 @cdr=nil, @car=3>, @car=2>, @car=1>


Let's print them pretty:

(def printelements(cons)
  (if (Cons === cons)
      (if (Cons === (car cons))
         (printlist (car cons))
        else
         (print (car cons))
      end)
      (if (Cons === (cdr cons))
         (print " ")
         (printelements (cdr cons))
       elsif (not (null (cdr cons)))
         (print " . ")
         (print (cdr cons))
      end)
  else
     (print cons)
  end)
end)

(def printlist(cons)
 (print "(")
 (printelements cons)
 (print ")")
 cons
end)

(def terpri()
  (print "\n")
end)


irb(main):263:0> (begin
                   (terpri)
                   (printlist (list 1,2,3))
                   (terpri)
                 end)

(1 2 3)
nil

You can also add some higher level abstractions:

(def first(list)
  (car list)
end)

(def rest(list)
  (cdr list)
end)

(def endp(list)
  (if (Cons === list)
      nil
   elsif (null list)
      true
   else
      (error ("Expected a list instead of the atom " + (list . to_s)))
   end)
end)



Once you've got this list abstraction, you can build functions such as
reverse:

(def revappend(list,tail)
 (if (null list)
     tail
  else
     (revappend (cdr list),(cons (car list),tail))
  end)
end)

(def reverse(list)
 (revappend list,nil)
end)



irb(main):267:0> (begin
                   (printlist (reverse (list 1,2,3)))
                   (terpri)
                 end)
(3 2 1)
nil



Now we also need the function abstraction.  In ruby functions have to
be in modules, and always qualified by the module name.  This is not
pretty, so we will allow any method to be treated as a function, but
we will ignore it's recipient object, always passing self.

# For methods, the first function argument is the recipient of the
# message:

(def method(designator,arity=(-1))
   # Important: the given arity must include the recipient object, but
not the recipient class:
   # (method :==,2) .call(42,42)   vs. 42.==(42)
   (if (arity == -1)
      # This is the default, bugged behavior:
      (Proc . new {| x , *args | (x . send(designator , *args))})
    elsif (arity == 0)
      (raise (Exception.exception("An instance method must have an
arity>=1, not 0.")))
    else
      (arity = (arity - 1))
      (args = (((arity == 0)? "" : " , ") + (((1 .. arity) . map { | i |
("a" + i.to_s) }) . join(" , "))))
      (eval("(Proc . new { | x" + args + " | " +
            "( x . send( :" + (designator . to_s) + args + " ))})"))
    end)
 end)


# for functions, the recipient of the message is always 'self', all
# arguments are passed as arguments.

(def function(designator,arity=(-1))
  # Important: the given arity must include the recipient object, but
not the recipient class:
  # (function "SOME_MODULE.someFunction",1) .call(42)   vs.
SOME_MODULE.someFunction(42)
  # (function :someFunction             ,2) .call(42)   vs.
self.someFunction(42) or someFunction(42)
  (if (String === designator)
     mod , met = (designator . split("."))
     (if (arity == -1)
        # This is the default, bugged behavior:
        (Proc . new {| *args | ((eval mod) . send((met . to_sym) ,
*args))})
      else
        (args = (((1 .. arity) . map { | i | ("a" + i.to_s) }) . join("
, ")))
        (sep = " ")
        (if (0 < arity)
           (sep = " , ")
         end)
        (eval("(Proc . new { | " + args + " | " +
              "( " + mod  + " . send( :" + met + sep + args + " ))})"))
      end)
   else
     (if (arity == -1)
        # This is the default, bugged behavior:
        (Proc . new {| x , *args | (x . send(designator , *args))})
      elsif (arity == 0)
        (eval("(Proc . new {" +
              "(" + (designator . to_s) + " )})"))
      else
        (args = (((1 .. arity) . map { | i | ("a" + i.to_s) }) . join("
, ")))
        (eval("(Proc . new { | " + args + " | " +
              "(" + (designator . to_s) + " " + args + " )})"))
      end)
   end)
end)


(def funcall(fun,*args)
  (fun . call(*args))
end)



irb(main):370:0> (funcall (method :+,2),3,4)
(funcall (method :+,2),3,4)
7

irb(main):478:0> (begin
                   (terpri)
                   (printlist (funcall (function :reverse,1),(list
1,2,3)))
                   (terpri)
                 end)

(3 2 1)
nil



Now that you have first class functions, you can start to implement
higher level functions:

(def mapcar (fun,list)
 (if (endp list)
    nil
  else
   (cons (funcall fun,(first list)),(mapcar fun,(rest list)))
  end)
end)


irb(main):271:0> (begin
                   (printlist (mapcar (lambda {|x| (x + 1)}),(list
1,2,3)))
                   (terpri)
                 end)

(2 3 4)
nil



So at least, now you can find the smallest element of a list:

This function implements an accumulator pattern: we pass the partial
result
along with the parameters.  We process the list item by item and when
its finished
we return the result we've accumulated so far:

(def smallestElement(list,minimum)
 (if (endp list)
     minimum
  elsif (minimum < (first list))
     (smallestElement (rest list),minimum)
  else
     (smallestElement (rest list),(first list))
  end)
end)

(def smallest(list)
 (smallestElement (rest list),(first list))
end)



irb(main):422:0>
(begin
   (terpri)
   (printlist (mapcar (function :smallest,1),(list (list 1),
                                                   (list 1,1,1,1),
                                                   (list 1,2,3,4),
                                                   (list 4,3,2,1),
                                                   (list 1,2,3,4,3,2,1),
                                                   (list
4,3,2,1,2,3,4))))
   (terpri)
end)

(1 1 1 1 1 1)
nil




Oh, and by the way, instead of using Matzacred Lisp aka Ruby, you could
as well use directly the original, Lisp, and without dilation type away:

C/USER[7]> (defun smallest (list)
             (labels ((smallest-element (list minimum)
                        (cond
                          ((endp list)              minimum)
                          ((< minimum (first list)) (smallest-element
(rest list) minimum))
                          (t                        (smallest-element
(rest list) (first list))))))
               (smallest-element (rest list) (first list))))
SMALLEST
C/USER[8]> (mapcar (function smallest) (list (list 1)
                                             (list 1 1 1 1)
                                             (list 1 2 3 4)
                                             (list 4 3 2 1)
                                             (list 1 2 3 4 3 2 1)
                                             (list 4 3 2 1 2 3 4)))
(1 1 1 1 1 1)
C/USER[9]>

( Try out http://clisp.cons.org or http://sbcl.sourceforge.net
  Have a look at http://www.gigamonkey.com/book )


> Some links to this subject would be nice also.

http://www.stanford.edu/class/cs242/readings/backus.pdf



For purely functional programming you could learn Haskel, or a language
of the ML family, but Common Lisp is more useful since it's a
multi-paradigm programming language: when you reach the limits of a
given paradigm, such as OO, or functional, you always have yet another
programming paradigm available in lisp (logical, declarative, you name
it).
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-09 06:44
Pascal J. Bourguignon wrote:
> [...]
>
> (def revappend(list,tail)
>  (if (null list)
>      tail
>   else
>      (revappend (cdr list),(cons (car list),tail))
>   end)
> end)
>
> [...]

I like how you sneaked in the parentheses.  It's a clever way to get
people accustomed to parens without actually using Lisp.  A way-point
on the road to enlightenment.  The too-many-parens complex is at the
same time a trivial psychological barrier and a roadblock to greater
understanding of the art of programming.

There comes a time when one finds oneself weary of evaling strings.
What's needed is a language that can represent itself.  To metaprogram
in the same language of the program--when the metalanguage is the
language--this is the weapon of a Lisper.  Not as clumsy or random as
a string eval; an elegant weapon for a more civilized age.
1b30afec8cd3efa4c7a10980c10315d8?d=identicon&s=25 Haris Bogdanovic (Guest)
on 2009-01-09 12:10
(Received via mailing list)
In Ruby everything is an object. That's my favourite Ruby's feature. And
you
showed that by just making one module you can have Lisp like functional
programming. So I'll stick with Ruby.

"Pascal J. Bourguignon" <pjb@informatimago.com> wrote in message
news:87vdspbf7i.fsf@informatimago.com...
1b30afec8cd3efa4c7a10980c10315d8?d=identicon&s=25 Haris Bogdanovic (Guest)
on 2009-01-09 12:20
(Received via mailing list)
I managed to find a minimum:

[1,6,4,7,8].inject {|a,b| if a < b then a else b end}

but I can't still find a way to sort list (functional way and not with
existing sort method)

Thanks

"Haris Bogdanovic" <fbogdanovic@xnet.hr> wrote in message
news:gk3m77$1ft$1@gregory.bnet.hr...
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-09 13:00
(Received via mailing list)
"Haris Bogdanovic" <fbogdanovic@xnet.hr> writes:

> In Ruby everything is an object. That's my favourite Ruby's feature. And you
> showed that by just making one module you can have Lisp like functional
> programming. So I'll stick with Ruby.

In Lisp too, everything is an object.  But there are several kinds of
objects, and you can create your own kinds too (you can write new
meta-classes in CLOS).

C/USER[15]> (defclass automobile () ())
#1=#<STANDARD-CLASS AUTOMOBILE>
C/USER[16]> (defvar *car* (make-instance 'automobile))
*CAR*
C/USER[17]> (class-of *car*)
#1=#<STANDARD-CLASS AUTOMOBILE>
C/USER[18]> (class-of 1)
#1=#<BUILT-IN-CLASS INTEGER>
C/USER[19]> (class-of "string")
#1=#<BUILT-IN-CLASS STRING>
C/USER[20]> (defstruct wheel)
WHEEL
C/USER[21]> (defclass automobile () ((wheels :accessor wheels :initform
(list (make-wheel) (make-wheel) (make-wheel) (make-wheel)))))
WARNING: DEFCLASS: Class AUTOMOBILE (or one of its ancestors) is being
redefined, instances are obsolete
#1=#<STANDARD-CLASS AUTOMOBILE :VERSION 1>
C/USER[22]> (wheels *car*)
(#S(WHEEL) #S(WHEEL) #S(WHEEL) #S(WHEEL))
C/USER[23]> (class-of (wheels *car*))
#1=#<BUILT-IN-CLASS CONS>
C/USER[24]> (class-of (first (wheels *car*)))
#1=#<STRUCTURE-CLASS WHEEL>
C/USER[25]>
1b30afec8cd3efa4c7a10980c10315d8?d=identicon&s=25 Haris Bogdanovic (Guest)
on 2009-01-09 13:20
(Received via mailing list)
I meant, every expression is an object. Not sure that's the case with
clisp.

"Pascal J. Bourguignon" <pjb@informatimago.com> wrote in message
news:87hc48bsgp.fsf@informatimago.com...
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-09 13:26
(Received via mailing list)
"Haris Bogdanovic" <fbogdanovic@xnet.hr> writes:

> I managed to find a minimum:
>
> [1,6,4,7,8].inject {|a,b| if a < b then a else b end}
>
> but I can't still find a way to sort list (functional way and not with
> existing sort method)


One difficulty is that vectors are difficult to handle with purely
functional programming: you cannot modify them, not a single slot; when
you want to change just one slot of a vector, in purely functionnal
programming, you have to define a whole new vector, that contains the
same elements as the old vector, but for the one that is "changed".

But you asked first for lists, and I showed you how to implement lists.
So now it should be easy to implement a functionnal sort algorithm
working on lists.

For an exemple here is a simple merge sort.  We need a function to merge
two lists:

(def mergelist(list1,list2,lessp)
   (if (endp list1)
      list2
    elsif (endp list2)
      list1
    elsif (funcall lessp,(first list1),(first list2))
      (cons (first list1),(mergelist (rest list1),list2,lessp))
    else
      (cons (first list2),(mergelist list1,(rest list2),lessp))
    end)
 end)


irb(main):545:0>
(begin
  (terpri)
  (printlist (mergelist (list 1,3,5,7,8,9,11),(list
2,4,6,10,11,12,13),(method :<)))
  (terpri)
end)

(1 2 3 4 5 6 7 8 9 10 11 11 12 13)
nil



Then a function to split a list into two parts separated by a pivot (one
list containing all the elements smaller-or-equal to the pivot, and the
other all the elements greater than the pivot):

(def values(*values)
   values
end)


(def splitlistinternal(list,pivot,lessp,less,more)
  (if (endp list)
     (values less,more)
  elsif (funcall lessp,pivot,(first list))
     (splitlistinternal (rest list),pivot,lessp,less,(cons (first
list),more))
  else
     (splitlistinternal (rest list),pivot,lessp,(cons (first
list),less),more)
  end)
end)

(def splitlist(list,pivot,lessp)
  (splitlistinternal list,pivot,lessp,nil,nil)
end)


(def valuelist(values)
   (list *values)
end)



irb(main):604:0>
(begin
   (terpri)
   (printlist (valuelist (splitlist (list 1,2,3,4,5,6,7),3,(method
:<))))
   (terpri)
end)

((3 2 1) (7 6 5 4))
nil


With these two utilities, writting a functional merge sort is trivial:

(def sortlist(list,lessp)
   (if ((endp list) or (endp (rest list)))
      list
    else
      (pivot = (first list))
      (less,more = (splitlist (rest list),pivot,lessp))
      (mergelist (sortlist less,lessp),(cons pivot,(sortlist
more,lessp)),lessp)
    end)
 end)


irb(main):635:0>
(begin
   (terpri)
   (printlist (mapcar (lambda{|list|
                          (sortlist list,(method :<))
                         }),(list (list),(list 1),(list 5,4,3,2,1),(list
1,2,3,4,5),(list 1,3,5,4,2),(list 5,3,1,2,4))))
   (terpri)
 end)

(nil (1) (1 2 3 4 5) (1 2 3 4 5) (1 2 3 4 5) (1 2 3 4 5))
nil
733df2bec21a6aa4eaaa38ff8dac96b6?d=identicon&s=25 andrea (Guest)
on 2009-01-09 13:40
(Received via mailing list)
Very interesting discussion, I love ruby and I love also haskell,
which I think made me a better programmer.

But does it really make sense to program in a functional way in ruby??

"Functional" code can be much more clean and elegant (in my opinion)
but what about performance issues???

The interpreter is going to have many new objects created around, and
so there will be more work for the garbage collector or more memory
used, am I wrong?
1b30afec8cd3efa4c7a10980c10315d8?d=identicon&s=25 Haris Bogdanovic (Guest)
on 2009-01-09 14:10
(Received via mailing list)
I would like if this sort could be made of Ruby functions;
inject,select,
detect, reject, collect, not with "module" you implemented. And it
doesnt't
have to be a list sorting; can be an array sorting or something. I'm not
an
expert in knowing all differences between all kinds of collections.
And what about recursion ? If I want to sort an array do I have to use
recursion and how ?
There are tutorials on functional programming in general but not with
Ruby.
Does anybody maybe has some link to something similar ?

"Pascal J. Bourguignon" <pjb@informatimago.com> wrote in message
news:87d4ewbrgz.fsf@informatimago.com...
1b30afec8cd3efa4c7a10980c10315d8?d=identicon&s=25 Haris Bogdanovic (Guest)
on 2009-01-09 14:25
(Received via mailing list)
Only the bits of a program that are appropriate for that.
I don't know how can a Haskell programmer live without objects in larger
projects ? You loose track of what is where in the program.

"andrea" <kerny404@gmail.com> wrote in message
news:c3f4685a-6558-4a75-93f0-a6b1c9061c63@n33g2000pri.googlegroups.com...
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2009-01-09 16:05
(Received via mailing list)
On Jan 9, 2009, at 6:39 AM, andrea wrote:

> But does it really make sense to program in a functional way in ruby??

That's a great question.

Functional languages have been a hot topic lately and it seems with
that comes the need to make all languages functional.  We've seen
plenty of that in Rubyland.  Try to count the number of actor style
networking libraries (heavily inspired by Erlang I'm guessing) we have
now.

But you've nailed the key question:  is this right for Ruby?  My
feeling is probably not.

Don't get me wrong.  I like functional languages.  I've learned a lot
from using them even just a little.  I think there are projects where
I would prefer them.

However, Ruby is almost a flat opposite to them in many ways.  Ruby
loves side effects and data is just about never locked down in Ruby.
That's not bad, it's just Ruby.

There may be times when we should adopt a functional approach here and
there.  but if you are trying to avoid calling standard iterators in
Ruby because they aren't functional, you're trading Ruby advantages
away for heavy Ruby minuses.  Performance is just one example of
this.  If you are willing to make that trade, why not trade languages
and get back to the things you want being an advantage?

Of course, that's all just my opinion.  I mean no offense.

James Edward Gray II
F690ec04b0501b74b033fc64ff4f682b?d=identicon&s=25 Dean Wampler (Guest)
on 2009-01-09 16:27
(Received via mailing list)
On Thu, Jan 8, 2009 at 10:09 AM, Pascal J. Bourguignon
<pjb@informatimago.com> wrote:
>
>
And Scala (if you like static typing - gasp! ;), OCaml and F# (I
believe).
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-09 17:01
(Received via mailing list)
"Haris Bogdanovic" <fbogdanovic@xnet.hr> writes:
>> objects, and you can create your own kinds too (you can write new
>> meta-classes in CLOS).
>
> I meant, every expression is an object. Not sure that's the case with clisp.


Expressions, in ruby are nothing.        Expression in lisp are objects.

The result of evaluating an expresssion  The result of evaluating an
expression
in ruby is an object.                    in lisp is an object.

Since expression in ruby are nothing     Since expressions in lisp are
objects
we stop here.                            some expressions may return
objects
                                         that are other expressions.

                                         Therefore you can write in lisp
                                         program that write programs
                                         (little Santa's helpers), more
                                         easily than you can in ruby.


C/USER[127]>  (let ((expression '(+ 1 2)))
                (values (class-of expression)
                        (eval expression)
                        (class-of (eval expression))))
#1=#<BUILT-IN-CLASS CONS> ;
3 ;
#1=#<BUILT-IN-CLASS INTEGER>
C/USER[128]>  (let ((expression '(list '+ 1 2)))
                (values (class-of expression)
                        (eval expression)
                        (class-of (eval expression))))
#1=#<BUILT-IN-CLASS CONS> ;
(+ 1 2) ;
#1=#<BUILT-IN-CLASS CONS>
C/USER[129]>


But of course, you can get it only when you become lazy and want to
automate not only a bank teller's job, but yours also.



(Of course, like in any programming language, you can in Ruby put
expressions into strings, and have the interpreter read again these
strings, and you can even use libraries (because it is hard enough
most ruby programmers couldn't do it themselves) to parse ruby text
into ruby objects (Array, Symbol, and other ruby Object), and back
from these R-expr into a string.  Oops, you don't get an expression
yet.  You have again to feed back the interpreter with this string, to
let it do again the parsing, build the expression in some internal
opaque and inaccessible object (not a ruby object, IIRC ruby is
written in C, that would mean that ruby expressions are actually C
structures, not ruby objects)).
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-09 17:01
(Received via mailing list)
andrea <kerny404@gmail.com> writes:

> used, am I wrong?
Who cares about processing time?  The processors are faster and faster
by the minute.  Memory and memory is bigger by the hour.

But programmer's time is still incompressible.  I still have only 24
hours per day.  This is the time that should be optimized.  And if
writting functional code makes us programmers spare development and
debugging time, so be it.

Of course, if ruby is not good at optimizing functional code, and this
posed any performance problem in the deployed applications, you would
better switch to Common Lisp or Haskell.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-09 18:05
Pascal J. Bourguignon wrote:
> Of course, if ruby is not good at optimizing functional code, and this
> posed any performance problem in the deployed applications, you would
> better switch to Common Lisp or Haskell.

Perhaps most importantly, Ruby doesn't optimise tail recursion (although
I think I read somewhere that ruby1.9 can do this, but only if you set a
flag when compiling the interpreter)

It's also true that cons(a,b) is going to be more efficient than [a] +
b, because the latter creates a whole new Array object and copies all
the elements. (This is of benefit primarily for algorithms which
generate new lists by prepending elements to the front of existing
lists)

However if you don't care about this, you can write your functional
algorithms using Ruby's Array as if it were a list.

class Array
  def car
    self[0]
  end
  def cdr
    self[1..-1]
  end
end

If you want though, you can create a true Cons object in Ruby. If you
make it Enumerable then you can keep much of the Ruby goodness in that
area.

class Cons
  include Enumerable
  attr_accessor :car, :cdr
  def initialize(car, cdr = nil)
    @car = car
    @cdr = cdr
  end
  def each(&blk)
    yield @car
    @cdr.each(&blk) if @cdr
  end
  class << self
    alias :[] :new
  end
end

l = Cons[1, Cons[2, Cons[3]]]
puts l.inject(0) { |acc,v| acc + v }
m = Cons[4, l]
puts m.inject(0) { |acc,v| acc + v }

p l.to_a    # using Enumerable#to_a
p m.to_a

You may find this more attractive than going the whole
"lets-pretend-Ruby-is-Lisp" approach. Similarly, I don't really see the
need to define a 'function' again from scratch, when you have lambda
{...} natively.
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-10 14:40
Brian Candler wrote:
>
> I don't really see the need to define a 'function' again from
> scratch, when you have lambda {...} natively.

Pascal was making a simulation of first-class functions in ruby.  The
difference between lambda { } and a real first-class function is quite
profound.

When I say that I prefer functional style in ruby, I mean that this

  def sum(array)
    array.inject(0) { |acc, x| acc + x }
  end

is better than this

  def sum(array)
    result = 0
    for x in array
      result += x
    end
    result
  end

In addition, chaining blocks/lambdas can often be terser/better than
the imperative equivalent.  When the details of the step-by-step
operations are abstracted, the result is smaller code with less room
for error.

I use functional-ness in ruby on a small scale, such as inside the
implementation of a method, but no wider.  For reasons I gave earlier,
functional style is most applicable to flat arrays and hashes, and
probably less so with more complex structures
(http://groups.google.com/group/comp.lang.ruby/brow...).
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-10 15:05
Mike Gold wrote:
> Pascal was making a simulation of first-class functions in ruby.  The
> difference between lambda { } and a real first-class function is quite
> profound.

For the benefit of this duffer, could you briefly explain the
difference? lambdas are "first-class enough" for me: I can pass them as
function arguments, I can return them from functions, and I can create
new instances of them which bind to different environments.

Now, having a look in Wikipedia:

"In computer science, a programming language is said to support
first-class functions (or function literal) if it treats functions as
first-class objects. Specifically, this means that the language supports
constructing new functions during the execution of a program, storing
them in data structures, passing them as arguments to other functions,
and returning them as the values of other functions. This concept
doesn't cover any means external to the language and program
(metaprogramming), such as invoking a compiler or an eval function to
create a new function."

Personally I consider Ruby lambdas to be first-class as they are.

It looks like the definition has been explicitly rigged to exclude
functions created by eval, without really explaining why. But even so, I
can create functions dynamically without eval, which differ by their
binding:

  def make_incrementer(n)
    return lambda { |x| x+n }
  end

Wikipedia continues:

"These features are a necessity for the functional programming style, in
which (for instance) the use of higher-order functions is a standard
practice. A simple example of a higher-ordered function is the map or
mapcar function, which takes as its arguments a function and a list, and
returns the list formed by applying the function to each member of the
list. For a language to support map, it must support passing a function
as an argument."

Of course, Ruby supports the map abstraction, and the passing of
functions (or blocks) to map, quite happily.

Now, map is just a "simple example", and perhaps Ruby supports only a
limited subset of functionality which includes map. Can you provide a
better example which shows how a Ruby lambda is not first-class?
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-10 15:50
Brian Candler wrote:
> Mike Gold wrote:
>> Pascal was making a simulation of first-class functions in ruby.  The
>> difference between lambda { } and a real first-class function is quite
>> profound.
>
> For the benefit of this duffer, could you briefly explain the
> difference?

It has already been shown.  You need to make a sincere effort to
understand the post to which you replied.  If you did not understand
Pascal's post, then ask a question.
Ae16cb4f6d78e485b04ce1e821592ae5?d=identicon&s=25 Martin DeMello (Guest)
on 2009-01-10 15:58
(Received via mailing list)
On Sat, Jan 10, 2009 at 5:39 PM, Mike Gold <mike.gold.4433@gmail.com>
wrote:
>
> I use functional-ness in ruby on a small scale, such as inside the
> implementation of a method, but no wider.  For reasons I gave earlier,
> functional style is most applicable to flat arrays and hashes, and
> probably less so with more complex structures
> (http://groups.google.com/group/comp.lang.ruby/brow...).

There are two, not-really-identical things that people mean when they
speak of "functional programming" - programming via pure,
side-effectless functions, and programming via higher order functions.
The former doesn't really make sense for ruby, as several people have
pointed out, but the latter is a useful technique, and pretty well
supported.

martin
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-10 16:59
> It has already been shown.  You need to make a sincere effort to
> understand the post to which you replied.  If you did not understand
> Pascal's post, then ask a question.

You mean this bit?

(def function(designator,arity=(-1))
...
        (args = (((1 .. arity) . map { | i | ("a" + i.to_s) }) . join("
, ")))
        (eval("(Proc . new { | " + args + " | " +
              "(" + (designator . to_s) + " " + args + " )})"))

??

Well, if I call

  function(:smallest, 1)

it returns

  eval "(Proc . new { | a1 | (smallest a1 )})"

I've never had to use any eval construct like this in real Ruby
programs. The nearest I can think of is obj.method(:smallest) which
gives a bound method object I can pass around and call later.

Is it trying to defeat auto-splat? This is certainly an area where Ruby
appears to be broken.

irb(main):001:0> def f(x); puts "*** #{x.inspect} ***"; end
=> nil
irb(main):002:0> ff = method(:f)
=> #<Method: Object#f>
irb(main):003:0> ff[1]
*** 1 ***
=> nil
irb(main):004:0> [1,2,3].map(&ff)
*** 1 ***
*** 2 ***
*** 3 ***
=> [nil, nil, nil]
irb(main):005:0> [[1],[2],[3]].map(&ff)
*** [1] ***
*** [2] ***
*** [3] ***
=> [nil, nil, nil]
irb(main):006:0> [[1],[2],[3,4]].map(&ff)
*** [1] ***
*** [2] ***
ArgumentError: wrong number of arguments (2 for 1)
  from (irb):1:in `f'
  from (irb):1:in `to_proc'
  from (irb):6:in `map'
  from (irb):6
  from :0

However, this problem doesn't occur if you use lambda rather than
method(:...)

irb(main):007:0> ff = lambda { |x| puts "*** #{x.inspect} ***" }
=> #<Proc:0xb7da2a70@(irb):7>
irb(main):008:0> [[1],[2],[3,4]].map(&ff)
*** [1] ***
*** [2] ***
*** [3, 4] ***
=> [nil, nil, nil]

so it seems to me that everything Pascal wrote could be written directly
in corresponding Ruby. For example:

# (begin
#   (printlist (mapcar (lambda {|x| (x + 1)}),(list 1,2,3)))
#   (terpri)
# end)

p [1,2,3].map { |x| x + 1}

# (def smallestElement(list,minimum)
#  (if (endp list)
#      minimum
#   elsif (minimum < (first list))
#      (smallestElement (rest list),minimum)
#   else
#      (smallestElement (rest list),(first list))
#   end)
# end)
#
# (def smallest(list)
#  (smallestElement (rest list),(first list))
# end)

smallestElement = lambda { |list,minimum|
  if list.empty?
    minimum
  elsif minimum < list.first
    smallestElement[list[1..-1],minimum]
  else
    smallestElement[list[1..-1],list.first]
  end
}

smallest = lambda { |list|
  smallestElement[list[1..-1], list.first]
}

# (begin
#    (terpri)
#    (printlist (mapcar (function :smallest,1),(list (list 1),
#                                                    (list 1,1,1,1),
#                                                    (list 1,2,3,4),
#                                                    (list 4,3,2,1),
#                                                    (list
1,2,3,4,3,2,1),
#                                                    (list
4,3,2,1,2,3,4))))
#    (terpri)
# end)

p [
  [1],
  [1,1,1,1],
  [1,2,3,4],
  [4,3,2,1],
  [1,2,3,4,3,2,1],
  [4,3,2,1,2,3,4]].map(&smallest)

I'm afraid I don't really have the patience to convert all of the rest.
If you are only going to talk in hints and enigmas, perhaps you could
hint at which point of all this Ruby breaks down?
1b30afec8cd3efa4c7a10980c10315d8?d=identicon&s=25 Haris Bogdanovic (Guest)
on 2009-01-10 18:00
(Received via mailing list)
So you want to say that common lisp is pure object oriented like ruby ?.

"Pascal J. Bourguignon" <pjb@informatimago.com> wrote in message
news:7c7i54eaoi.fsf@pbourguignon.anevia.com...
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2009-01-10 18:20
(Received via mailing list)
On 10.01.2009 15:50, Mike Gold wrote:
> Pascal's post, then ask a question.
Frankly, I find this reply of yours at least unhelpful if not hostile
(which might just be an effect of brevity).  That question (which was
quite precise, even contrasting a definition of "first class functions"
with Ruby's capabilities) actually refers to a statement that you made
so you could at least point to the explanation you have seen.

 From what I see Pascal creates a DSL that mimics Lisp in Ruby - with
all the basic functions like cons, car and all the bracketing (which is
by no means needed for a functional language).  He also contrasts Ruby's
capabilities with (Common) Lisp's.  But: Lisp is just one functional
language among many and the fact that expressions are objects in Lisp
and the special feature of Lisp macros is not mandatory for a functional
language as far as I can see.

http://en.wikipedia.org/wiki/Functional_programming

So, while we can agree that Ruby != Lisp - and especially Ruby lacks
Lisp's macros and access to expressions as objects - there is nothing
that prevents functional programming in Ruby at all.  And you do neither
need Lisp's syntax for this nor Lisp's macros.

Back to Brian's original question: When reading through Pascal's
statements in this thread the only thing that I can see so far is that
Ruby cannot treat arbitrary expressions as objects.  But as I said, this
does not seem to be a mandatory feature of a functional language.

http://en.wikipedia.org/wiki/First-class_function

I tend to agree with Martin DeMello's statement about the two core
concepts of functional programming.  The only thing I would add is that
while programming without side effects might not make sense in Ruby it
is possible nevertheless.  You pay a price in GC overhead but this is
irrelevant for answering whether Ruby has functional capabilities.

Regards

  robert
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-10 18:26
(Received via mailing list)
Brian Candler <b.candler@pobox.com> writes:

>         (eval("(Proc . new { | " + args + " | " +
>   eval "(Proc . new { | a1 | (smallest a1 )})"
>
> I've never had to use any eval construct like this in real Ruby
> programs.

That's because Matzacred lisp lacks macros, because it's not an
homoiconic
language.


> The nearest I can think of is obj.method(:smallest) which
> gives a bound method object I can pass around and call later.

Why should it be bound?  Why if we wanted to map over recipient objects?
Why make a difference between the reciever of a message and the other
arguments of a method?


> Is it trying to defeat auto-splat? This is certainly an area where Ruby
> appears to be broken.

In part yes.

> *** 3 ***
>   from (irb):1:in `f'
> irb(main):008:0> [[1],[2],[3,4]].map(&ff)
> *** [1] ***
> *** [2] ***
> *** [3, 4] ***
> => [nil, nil, nil]

Are you saying that you must use lambda everywhere, even when you have a
named function?  That named functions are less than anonymous functions?
Indeed, that's why I introduced (function :name,arity) and (method
:name,arity), to put them to the same level as lambda (Proc instances).

> # (def smallestElement(list,minimum)
> #  (smallestElement (rest list),(first list))
> }
>
> smallest = lambda { |list|
>   smallestElement[list[1..-1], list.first]
> }

There's a difference between (def smallest(x) ; x ; end)
and (smallest = (lambda { |x| x }))

In the former case, you can write (smallest [1,2,3])
in the later you can't:


irb(main):001:0> (def smallest(x) ; x ; end)
(def smallest(x) ; x ; end)
nil
irb(main):002:0> (smallest [1,2,3])
(smallest [1,2,3])
[1, 2, 3]
irb(main):003:0> (smallest = (lambda { |x| x }))
(smallest = (lambda { |x| x }))
#<Proc:0x0035fdf4@(irb):3>
irb(main):004:0> (smallest [1,2,3])
(smallest [1,2,3])
(irb):3: warning: multiple values for a block parameter (3 for 1)
  from (irb):4
[1, 2, 3]


There's also this brokenness that once you assign a variable, you can't
call the function of same name anymore:

irb(main):005:0> (def smallest(x) ; x ; end)
(def smallest(x) ; x ; end)
nil
irb(main):006:0> (smallest [1,2,3])
(smallest [1,2,3])
(irb):3: warning: multiple values for a block parameter (3 for 1)
  from (irb):6
[1, 2, 3]
irb(main):007:0> (smallest = nil)
(smallest = nil)
nil
irb(main):008:0> (smallest [1,2,3])
(smallest [1,2,3])
NoMethodError: undefined method `[]' for nil:NilClass
  from (irb):8
irb(main):009:0>

> #    (terpri)
> I'm afraid I don't really have the patience to convert all of the rest.
> If you are only going to talk in hints and enigmas, perhaps you could
> hint at which point of all this Ruby breaks down?

As you show it, it is perfectly possible to do it this way in ruby.  But
it's more complex. You have to know now what this & syntax does.  You
have to wonder why you cannot write:


(def biggest(x)
  ...
end)

 [
   [1],
   [1,1,1,1],
   [1,2,3,4],
   [4,3,2,1],
   [1,2,3,4,3,2,1],
   [4,3,2,1,2,3,4]].map(&biggest)

                                                                                                                                                 ArgumentError:
wrong number of arguments (0 for 1)
  from (irb):17:in `biggest'
  from (irb):17

Nor:

 [
   [1],
   [1,1,1,1],
   [1,2,3,4],
   [4,3,2,1],
   [1,2,3,4,3,2,1],
   [4,3,2,1,2,3,4]].map(biggest)
                                                                                                                                                 ArgumentError:
wrong number of arguments (0 for 1)
  from (irb):26:in `biggest'
  from (irb):26
irb(main):027:0>


So I concede that you can write what I wrote in Ruby (I wrote it in
Ruby!), but the way I wrote it is more homogenous, and therefore easier
to write, even if it means greenspunning some correct Lisp over the
ruins of Ruby.
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-10 18:28
(Received via mailing list)
Martin DeMello <martindemello@gmail.com> writes:

> side-effectless functions, and programming via higher order functions.
> The former doesn't really make sense for ruby, as several people have
> pointed out, but the latter is a useful technique, and pretty well
> supported.

The interest of writting purely functionnal code is not relative to a
language.  It's the ease of doing so that is relative to the language.

Purely functionnal code has the big advantage that once you've proved
and/or tested it, absolutely nothing else in the program can impact the
validity of this code: you will always get the same results for the same
arguments.
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-10 18:30
(Received via mailing list)
"Haris Bogdanovic" <fbogdanovic@xnet.hr> writes:
> So you want to say that common lisp is pure object oriented like ruby ?.

Well, beside being multiparadigm, yes, Common Lisp includes the best
object system there is in the world, and what's incredible, the first
standardized one too. (A shame for the followers).
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-10 19:12
Pascal J. Bourguignon wrote:
> Are you saying that you must use lambda everywhere, even when you have a
> named function?  That named functions are less than anonymous functions?

No. I was trying: (1) to understand what the underlying problem was that
you were trying to solve in jumping through all these hoops, rather than
using lambda { ... } directly, and (2) to understand Mike's assertion
that Ruby lambdas are not first-class functions (i.e. "The difference
between lambda { } and a real first-class function is quite profound").

>> smallest = lambda { |list|
>>   smallestElement[list[1..-1], list.first]
>> }
>
> There's a difference between (def smallest(x) ; x ; end)
> and (smallest = (lambda { |x| x }))
>
> In the former case, you can write (smallest [1,2,3])
> in the later you can't:

Sure, in the latter you'd write smallest[[1,2,3]]. The outer brackets
delimit the call of the lambda, and the inner ones mark the array.

> irb(main):003:0> (smallest = (lambda { |x| x }))
> (smallest = (lambda { |x| x }))
> #<Proc:0x0035fdf4@(irb):3>
> irb(main):004:0> (smallest [1,2,3])
> (smallest [1,2,3])
> (irb):3: warning: multiple values for a block parameter (3 for 1)
>   from (irb):4
> [1, 2, 3]

As far as I can see, it is only accidental that this runs at all.
'smallest' is not a method, it's a local variable, so smallest[...] can
only be interpreted as a call to the #[] method on that object, i.e.
smallest.call(...)

> As you show it, it is perfectly possible to do it this way in ruby.  But
> it's more complex. You have to know now what this & syntax does.  You
> have to wonder why you cannot write:
>
>
> (def biggest(x)
>   ...
> end)
>
>  [
>    [1],
>    [1,1,1,1],
>    [1,2,3,4],
>    [4,3,2,1],
>    [1,2,3,4,3,2,1],
>    [4,3,2,1,2,3,4]].map(&biggest)
>
> 
ArgumentError:
> wrong number of arguments (0 for 1)
>   from (irb):17:in `biggest'
>   from (irb):17

I would have expected map(&method(:biggest)) to work, but unfortunately
it doesn't due to arity reasons.

I guess you rob Peter to pay Paul. In Ruby, a bareword like "biggest"
can be a method name (in which case it invokes the method, and evaluates
to its return value), or a variable name (in which case it evaluates to
the content of that variable).

This means that common cases in Ruby don't need any syntactic markers
like () to say "invoke this method". The code just looks cleaner. But in
that case, if you want to refer to the *name* of the method or the
*method/function itself* then you need to mark in that case instead.
i.e. :foo or method(:foo)
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-10 21:00
(Received via mailing list)
Brian Candler <b.candler@pobox.com> writes:

> Pascal J. Bourguignon wrote:
>> Are you saying that you must use lambda everywhere, even when you have a
>> named function?  That named functions are less than anonymous functions?
>
> No. I was trying: (1) to understand what the underlying problem was that
> you were trying to solve in jumping through all these hoops, rather than
> using lambda { ... } directly,

lambda makes an anonymous function. I didn't want to use an anonymous
function, I wanted to use an existing function.

There's a secondary problem I tried to solve: Ruby has no named
function, it only has named methods.  When you call a method, you have
to pass a recipient object as first parameter.


> and (2) to understand Mike's assertion
> that Ruby lambdas are not first-class functions (i.e. "The difference
> between lambda { } and a real first-class function is quite profound").

> Sure, in the latter you'd write smallest[[1,2,3]]. The outer brackets
> delimit the call of the lambda, and the inner ones mark the array.

Yet another ad-hoc syntax.  Where does it come from?  How many other
such syntaxes are hidden?  Can't there be a simple language you can
learn in half a day? Yes: Lisp.

> I guess you rob Peter to pay Paul. In Ruby, a bareword like "biggest"
> can be a method name (in which case it invokes the method, and evaluates
> to its return value), or a variable name (in which case it evaluates to
> the content of that variable).

In Lisp, it can be both, and you always know which you refer.

(defun foo (x)
   (1+ x))

(let ((foo 41))
   (foo foo))
--> 42


And if you happen to store functions in a variable, you have the
operators FUNCTION and FUNCALL to switch from one namespace to the
other:

(let ((foo (lambda (x) (1- x))))
  (list (funcall (function foo) 0)  ; calls the function foo
        (foo 0)                     ; idem
        (funcall foo 0)))  ; calls the function bound to the variable
foo.
--> (1 1 -1)


> This means that common cases in Ruby don't need any syntactic markers
> like () to say "invoke this method".

But parentheses are necessary anyways (see for example the thread about
s = "thingy" if ((1 + 1) != 2) ).
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-10 21:57
Pascal J. Bourguignon wrote:
> lambda makes an anonymous function. I didn't want to use an anonymous
> function, I wanted to use an existing function.

But you can store the lambda under a name. My knowledge of Lisp is very
limited, but according to my copy of Abelson and Sussman (2nd ed, pp
62-63),

  (define (plus4 x) (+ x 4))

"is equivalent to"

  (define plus4 (lambda (x) (+ x 4)))

So I don't see what's wrong with writing

  plus4 = lambda { |x| x + 4 }

in Ruby, when that's all you're doing in Lisp. (Or Plus4, if you
consider a constant to be more in the spirit of 'define').

But we could be talking at Scheme/Common Lisp cross-purposes here.

> Can't there be a simple language you can
> learn in half a day? Yes: Lisp.

Well, it's easy for a Lisp programmer to say that "Lisp is simple". It's
also easy for me, as a programmer who started in machine code, assembly
language and C, to say that attempting to write anything substantial in
Lisp makes my head spin. These are arguably statements about ourselves,
rather than about the languages.

Ruby has syntax to expose various functionality. Lisp has special forms
like (quote ...) and (define ...) and (set! ... ) to expose similar
functionality. To you, the fact that completely different semantics can
be obtained using the same syntax as function application is a benefit.
To me, it is a hindrance.

It also may boil down to trivia such as which editor we use. One
probably can't write substantial Lisp without the help of an editor that
understands the syntax (and definitely Lisp's simple syntax is a big
benefit there). But to me, emacs is totally impenetrable. I can't even
exit the damn thing without going across to another shell and issuing a
'kill'.

I use joe and vi, and am very happy with both for what I do. But that in
turn probably means my hands are tied against Lisp.

>> I guess you rob Peter to pay Paul. In Ruby, a bareword like "biggest"
>> can be a method name (in which case it invokes the method, and evaluates
>> to its return value), or a variable name (in which case it evaluates to
>> the content of that variable).
>
> In Lisp, it can be both, and you always know which you refer.
>
> (defun foo (x)
>    (1+ x))
>
> (let ((foo 41))
>    (foo foo))
> --> 42

But isn't that the same distinction I was talking about, to refer to a
function as a value, versus applying it to some arguments?

Lisp:  foo versus (foo)

C:     foo versus foo()

Ruby:  method(:foo) versus foo         (for methods)
       foo versus foo.call() or foo[]  (for lambdas)

Most of the time you're invoking methods, not passing them around as
first order functions. And then mostly you're invoking a single method
with a single set of arguments.

Should methods and blocks/lambdas be the same thing in Ruby? Maybe. But
actually, the distinction works well in practice.

There are few rules you have to learn early on when programming in Ruby,
but one of those is how it disambiguates between a local variable and a
method call. The rule only works because 'def' starts a new scope.

This saves a lot of brackets, and without having to declare local
variables.

> And if you happen to store functions in a variable, you have the
> operators FUNCTION and FUNCALL to switch from one namespace to the
> other:
>
> (let ((foo (lambda (x) (1- x))))
>   (list (funcall (function foo) 0)  ; calls the function foo
>         (foo 0)                     ; idem
>         (funcall foo 0)))  ; calls the function bound to the variable
> foo.
> --> (1 1 -1)

Unless you are talking about Scheme where they share the same namespace.

Anyway, we've strayed way off the track. I will summarise as follows:

1. Ruby is rubbish because it has wrinkly syntax.

2. LISP is rubbish because there are a zillion incompatible versions of
it, and is impossible to write without a list-aware editor.

Then hopefully everyone will be equally unhappy :-)
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-10 23:11
(Received via mailing list)
Brian Candler <b.candler@pobox.com> writes:

> "is equivalent to"
>
>   (define plus4 (lambda (x) (+ x 4)))

This is Scheme, not Lisp.  Scheme is a lisp-1, not a lisp-2.  In Scheme,
variables and functions share the same namespace.

But this is not the case of Common Lisp and neither of Ruby.


> So I don't see what's wrong with writing
>
>   plus4 = lambda { |x| x + 4 }
>
> in Ruby, when that's all you're doing in Lisp. (Or Plus4, if you
> consider a constant to be more in the spirit of 'define').

Ruby doesn't work like that.

irb(main):033:0> plus4 = lambda { |x| x + 4}
plus4 = lambda { |x| x + 4}
#<Proc:0x00325230@(irb):33>
irb(main):034:0> (plus4 2)
(plus4 2)
NoMethodError: undefined method `plus4' for main:Object
  from (irb):34
irb(main):035:0>



> But we could be talking at Scheme/Common Lisp cross-purposes here.

Indeed.


> It also may boil down to trivia such as which editor we use. One
> probably can't write substantial Lisp without the help of an editor that
> understands the syntax (and definitely Lisp's simple syntax is a big
> benefit there). But to me, emacs is totally impenetrable. I can't even
> exit the damn thing without going across to another shell and issuing a
> 'kill'.
>
> I use joe and vi, and am very happy with both for what I do. But that in
> turn probably means my hands are tied against Lisp.

Note that there is a sizeable group of lisp programmers who don't use
emacs, but vim or other IDEs.
http://cybertiggyr.com/15-vim/
http://www.cliki.net/vim



>> (let ((foo 41))
> Ruby:  method(:foo) versus foo         (for methods)
> but one of those is how it disambiguates between a local variable and a
> method call. The rule only works because 'def' starts a new scope.
>
> This saves a lot of brackets, and without having to declare local
> variables.

Anyways, this part of the discussions raised to justify my papering over
these distinctions, and to have an homogeneous treatment of methods,
'functions' and lambdas when we want to do functional programming in
Ruby.  Indeed, there are distinctions made by Ruby.  Indeed, for
functional programming these distinctions are an hindrance both
syntactically and semantically, hence the definitions I gave.


> [...]
> Anyway, we've strayed way off the track. I will summarise as follows:
>
> 1. Ruby is rubbish because it has wrinkly syntax.
>
> 2. LISP is rubbish because there are a zillion incompatible versions of
> it, and is impossible to write without a list-aware editor.
>
> Then hopefully everyone will be equally unhappy :-)

LISP, yes.  But since that time, we standardized, and now there's one
true Lisp: Common Lisp.  And you can easily write programs portable
across different implementations of Common Lisp.  And see above, you can
write lisp in any editor, even vim.
50b2daf0e7666574579b9edaf8f2b69a?d=identicon&s=25 Pit Capitain (Guest)
on 2009-01-11 02:00
(Received via mailing list)
2009/1/10 Pascal J. Bourguignon <pjb@informatimago.com>:
>
> (let ((foo 41))
>   (foo foo))
> --> 42

Nothing special. In Ruby, that's:

  def foo(x)
    x + 1
  end

  foo = 41
  foo(foo)  # => 42

> And if you happen to store functions in a variable, you have the
> operators FUNCTION and FUNCALL to switch from one namespace to the
> other:
>
> (let ((foo (lambda (x) (1- x))))
>  (list (funcall (function foo) 0)  ; calls the function foo
>        (foo 0)                     ; idem
>        (funcall foo 0)))  ; calls the function bound to the variable foo.
> --> (1 1 -1)

Can be done as well in Ruby:

  foo = lambda { |x| x - 1 }
  [
    send(:foo, 0),
    foo(0),
    foo.call(0),
  ]  # => [1, 1, -1]

BTW: Why would I write (1- x) if I want x - 1 ?

Regards,
Pit
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-11 02:21
(Received via mailing list)
Pit Capitain <pit.capitain@gmail.com> writes:

>>   (1+ x))
>
>>        (funcall foo 0)))  ; calls the function bound to the variable foo.
>> --> (1 1 -1)
>
> Can be done as well in Ruby:
>
>   foo = lambda { |x| x - 1 }
>   [
>     send(:foo, 0),
>     foo(0),
>     foo.call(0),
>   ]  # => [1, 1, -1]

Ok.  Last time I tried, I got confusing results.


> BTW: Why would I write (1- x) if I want x - 1 ?

+1 is a number.   1+ is a symbol  (the syntax  1- corresponds to no
number).
-1 is a number.   1- is a symbol  (the syntax  1- corresponds to no
number).

So we can use these symbols to name functions
(defun 1+ (x) (+ x 1))
(defun 1- (x) (- x 1))

so we can (mapcar (function 1+) (list 1 2 3)) or (mapcar (function 1-)
(list 1 2 3))
without having to introduce yet another anonymous function.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-11 09:46
Pascal J. Bourguignon wrote:
> Ruby doesn't work like that.
>
> irb(main):033:0> plus4 = lambda { |x| x + 4}
> plus4 = lambda { |x| x + 4}
> #<Proc:0x00325230@(irb):33>
> irb(main):034:0> (plus4 2)
> (plus4 2)
> NoMethodError: undefined method `plus4' for main:Object
>   from (irb):34
> irb(main):035:0>

plus4.call(2), or more briefly, plus4[2]

Yes, a variable holding a lambda is a different beast to a method. As
you said, methods are associated with objects, and lambdas are not.
Usually one suits the usage case better than the other.

I find the distinct behaviours of both to be very useful in practice.
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-11 23:49
Brian Candler wrote:
> (2) to understand Mike's assertion
> that Ruby lambdas are not first-class functions (i.e. "The difference
> between lambda { } and a real first-class function is quite profound").

Ruby:

  mean = lambda { |*args|
    (args.inject(0) { |acc, x| acc + x })/args.size.to_f
  }
  data = [[1,2,3], [4,5,6,7]]

  data.map(&mean) # fail
  data.map(&:mean) # fail
  data.map { |*numbers| mean.call(*numbers) } # fail
  data.map { |numbers| mean.call(*numbers) } # phew, found it

Haskell:

  map mean data

On this example alone, I think my characterization of "quite profound"
is justified.
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-11 23:58
Brian Candler wrote:
>
> Yes, a variable holding a lambda is a different beast to a method. As
> you said, methods are associated with objects, and lambdas are not.
> Usually one suits the usage case better than the other.
>
> I find the distinct behaviours of both to be very useful in practice.

Can you give an example of where the difference is "very useful"?  In
every case I've encountered, it has been a hassle.

Because programmers can and should always be refactoring and changing
their mind, this entails the drudgework of adding/removing the
'.call()'s or the '[]'s or (in ruby 1.9) the '.()'s.  The worst
outcome is when a programmer doesn't bother to refactor because of the
hassle.

By the way, since locals are determined at parse time, I don't see any
particular reason why this cannot work:

  bisect = lambda { |x, y| (x + y)/2.0 }
  # ...
  bisect(5, 6)

Whether or not that _should_ work is a different story.  The pro is
that I can swap lambdas and methods without changing syntax.  The con
is, at least, that the meaning of existing code could change (when a
bisect method already exists).
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-12 00:05
Mike Gold wrote:
>
> By the way, since locals are determined at parse time, I don't see any
> particular reason why this cannot work:
>
>   bisect = lambda { |x, y| (x + y)/2.0 }
>   # ...
>   bisect(5, 6)
>
> Whether or not that _should_ work is a different story.  The pro is
> that I can swap lambdas and methods without changing syntax.  The con
> is, at least, that the meaning of existing code could change (when a
> bisect method already exists).

I see this is a bad idea due to the cases where the lambda is not a
local variable.

  [lambda { |x, y| (x + y)/2.0 }].first(5, 6)

Since the rule couldn't apply here, the special-case exception for a
local lambda would be silly.
2ee1a7960cc761a6e92efb5000c0f2c9?d=identicon&s=25 William James (Guest)
on 2009-01-12 00:27
(Received via mailing list)
Brian Candler wrote:

> # Example: convert all hash keys to strings
> src = {:one=>1, :two=>2}
>
> # Imperative
> out = src.inject({}) { |h,(k,v)| h[k.to_s] = v; h }
>
> # Functional
> out = src.inject({}) { |h,(k,v)| h.merge(k.to_s => v) }


src = {:one,1, :two,2}
    ==>{:one=>1, :two=>2}
Hash[ *src.map{|k,v| [k.to_s,v]}.flatten ]
    ==>{"two"=>2, "one"=>1}
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2009-01-12 00:34
(Received via mailing list)
Mike Gold wrote:
>   data = [[1,2,3], [4,5,6,7]]
> On this example alone, I think my characterization of "quite profound"
> is justified.

Why isn't this the ruby candidate for comparison:

mean = lambda { |ary|
   (ary.inject { |acc, x| acc + x })/ary.size.to_f
}
data.map(&mean) # => [2.0, 5.5]
2ee1a7960cc761a6e92efb5000c0f2c9?d=identicon&s=25 William James (Guest)
on 2009-01-12 00:55
(Received via mailing list)
Pascal J. Bourguignon wrote:

>
>
> irb(main):042:0> (car (cons 1,2))
>
>         (i = (i - 1))
>
>          (print " ")
> (def printlist(cons)
>
>
>       nil
> as reverse:
>  (revappend list,nil)
>
> (def method(designator,arity=(-1))
> i | ("a" + i.to_s) }) . join(" , "))))       (eval("(Proc . new { |
> not the recipient class:    # (function "SOME_MODULE.someFunction",1)
>            (sep = " , ")
>               "(" + (designator . to_s) + " )})"))
>   (fun . call(*args))
>                    (printlist (funcall (function :reverse,1),(list
> 1,2,3)))                    (terpri)
>                  end)
>
> (3 2 1)
> nil


You have certainly proved that everything is 10 times as hard
as it should be when you use Commune Lisp!

Ruby:

[1,2,3].reverse
    ==>[3, 2, 1]

[1,2,3].send :reverse
    ==>[3, 2, 1]



>    (cons (funcall fun,(first list)),(mapcar fun,(rest list)))
> nil
>


Ruby:

[1,2,3].map{|x| x + 1}
    ==>[2, 3, 4]


or:

augment = proc{|x| x + 1}
    ==>#<Proc:0x0282c2cc@(irb):11>
[1,2,3].map &augment
    ==>[2, 3, 4]

>      minimum
>
> 1,2,3,4,3,2,1),
> (list 4,3,2,1,2,3,4))))    (terpri)
> end)
>
> (1 1 1 1 1 1)
> nil


Ruby:

[[1],[1,1,1,1],[1,2,3,4],[4,3,2,1],[1,2,3,4,3,2,1],[4,3,2,1,2,3,4]].
map{|a| a.min}
    ==>[1, 1, 1, 1, 1, 1]



Yes, COBOL Lisp is an ancient, cumbersome, and clunky language.
Ruby is to it as a transistor is to a vacuum tube.
2ee1a7960cc761a6e92efb5000c0f2c9?d=identicon&s=25 William James (Guest)
on 2009-01-12 01:16
(Received via mailing list)
Pascal J. Bourguignon wrote:

> irb(main):002:0> (smallest [1,2,3])
> (smallest [1,2,3])
> [1, 2, 3]
> irb(main):003:0> (smallest = (lambda { |x| x }))
> (smallest = (lambda { |x| x }))
> #<Proc:0x0035fdf4@(irb):3>
> irb(main):004:0> (smallest [1,2,3])
> (smallest [1,2,3])
> (irb):3: warning: multiple values for a block parameter (3 for 1)
>   from (irb):4
> [1, 2, 3]

foo = proc{|x| "#{x.size} items: #{ x.join ' & ' }" }
    ==>#<Proc:0x0282ded8@(irb):15>
foo.call( [2,3,4] )
    ==>"3 items: 2 & 3 & 4"
foo[ [2,3,4] ]
    ==>"3 items: 2 & 3 & 4"
2ee1a7960cc761a6e92efb5000c0f2c9?d=identicon&s=25 William James (Guest)
on 2009-01-12 01:25
(Received via mailing list)
Joel VanderWerf wrote:

> >     (args.inject(0) { |acc, x| acc + x })/args.size.to_f
> >   map mean data
> >
> > On this example alone, I think my characterization of "quite
> > profound" is justified.
>
> Why isn't this the ruby candidate for comparison:
>
> mean = lambda { |ary|
>    (ary.inject { |acc, x| acc + x })/ary.size.to_f
> }
> data.map(&mean) # => [2.0, 5.5]

mean = proc{|a| a.inject{|s,x| s+x} / a.size.to_f }
    ==>#<Proc:0x027949e0@(irb):19>
data.map &mean
    ==>[2.0, 5.5]
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-12 02:02
Joel VanderWerf wrote:
> Mike Gold wrote:
>>   data = [[1,2,3], [4,5,6,7]]
>> On this example alone, I think my characterization of "quite profound"
>> is justified.
>
> Why isn't this the ruby candidate for comparison:
>
> mean = lambda { |ary|
>    (ary.inject { |acc, x| acc + x })/ary.size.to_f
> }
> data.map(&mean) # => [2.0, 5.5]

Sure, you can solve a different problem than the one I gave.
But the original problem remains.  We cannot, in general, modify
'mean'.

Do you propose to change every method in the ruby standard library
to take just one argument--an array?  That would work.  Sounds like
Lisp.
2ee1a7960cc761a6e92efb5000c0f2c9?d=identicon&s=25 William James (Guest)
on 2009-01-12 02:15
(Received via mailing list)
Haris Bogdanovic wrote:

> news:87d4ewbrgz.fsf@informatimago.com...  >"Haris Bogdanovic"
> > One difficulty is that vectors are difficult to handle with purely
> > For an exemple here is a simple merge sort.  We need a function to
> >     (cons (first list2),(mergelist list1,(rest list2),lessp))
> >
> >  values
> > list),less),more) end)
> >
> > nil
> >     (less,more = (splitlist (rest list),pivot,lessp))
> >                       }),(list (list),(list 1),(list
> > 5,4,3,2,1),(list  1,2,3,4,5),(list 1,3,5,4,2),(list 5,3,1,2,4))))
> > (terpri) end)
> >
> > (nil (1) (1 2 3 4 5) (1 2 3 4 5) (1 2 3 4 5) (1 2 3 4 5))
> > nil
> >
> >


Ruby:

def merge list1, list2, &less
  return list1 + list2  if list1.empty? or list2.empty?
  less = ( less or proc{|a,b| a < b} )
  less[ list1[0], list2[0] ] and
    [ list1[0] ] + merge( list1[1..-1], list2, &less ) or
    [ list2[0] ] + merge( list1, list2[1..-1], &less )
end

p merge( [1,3,5], [2,4,6] )
p merge( [-1,-3,-5], [-2,-4,-6] ){|a,b| a > b}

def merge_sort list
  return list  if list.size < 2
  half = list.size / 2
  merge merge_sort( list[0...half] ), merge_sort( list[half..-1] )
end

p merge_sort( [9,2,8,5,6,3] )
5.times{|i| p merge_sort( (0..i).to_a.reverse ) }
2ee1a7960cc761a6e92efb5000c0f2c9?d=identicon&s=25 William James (Guest)
on 2009-01-12 02:20
(Received via mailing list)
Mike Gold wrote:

> It's a clever way to get
> people accustomed to parens without actually using Lisp.  A way-point
> on the road to enlightenment.


There you have it.  Hierophants of Commune Lisp are utter irrational.
It's a religion to them.  In comp.lang.lisp you will find threads
named "How did you discover Lisp?", i.e., "How did you get religion?"
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2009-01-12 02:32
(Received via mailing list)
On Fri, Jan 9, 2009 at 1:39 PM, andrea <kerny404@gmail.com> wrote:
>
> The interpreter is going to have many new objects created around, and
> so there will be more work for the garbage collector or more memory
> used, am I wrong?
Yes or let us say you will be wrong very soon :).

Have a look at this to understand why:
http://rubyconf2008.confreaks.com/how-ruby-can-be-fast.html

HTH
Robert
2ee1a7960cc761a6e92efb5000c0f2c9?d=identicon&s=25 William James (Guest)
on 2009-01-12 02:35
(Received via mailing list)
Brian Candler wrote:

> manages to achieve ~50% of the speed of hand-written C. Given that C
> can be considered portable machine code, this is a pretty impressive
> achievement.

Don't take Ed seriously.  You will find that he never posts any
Ruby code that he has written.

And he has already been taught that OCaml can be as fast as C:

http://groups.google.com/group/comp.lang.ruby/brow...
1fb5b6a47f6
2ee1a7960cc761a6e92efb5000c0f2c9?d=identicon&s=25 William James (Guest)
on 2009-01-12 02:36
(Received via mailing list)
William James wrote:

>  utter irrational.

utterly
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-12 03:17
William James wrote:
> Mike Gold wrote:
>
>> It's a clever way to get
>> people accustomed to parens without actually using Lisp.  A way-point
>> on the road to enlightenment.
>
>
> There you have it.  Hierophants of Commune Lisp are utter irrational.
> It's a religion to them.  In comp.lang.lisp you will find threads
> named "How did you discover Lisp?", i.e., "How did you get religion?"

Ironically, a distinguishing characteristic of zealotry is the
inability to recognize waggishness or sarcasm.

There are several clues you could have picked up.

(1) If you are at all familiar with Star Wars, you could have noticed
that I paraphrased Obi-Wan Kenobi in the same post.

(2) Even if you've never heard of Star Wars, the hyperbolic nature of
my post should have been obvious.

(3) If you cared to check whether your assumptions had any merit, you
could have noticed the 40 non-trolling posts I've made to
comp.lang.ruby.  No posts to comp.lang.lisp.

(4) I contributed a small patch to Ruby; I appear in the ChangeLog.

On the other hand, your posting history reveals that you persistently
troll comp.lang.lisp with Ruby evangelism, much to the annoyance of
its inhabitants.

  "If you hate a person, you hate something in him that is part of
  yourself. What isn't part of ourselves doesn't disturb us."
    --Herman Hesse
3bb23e7770680ea44a2d79e6d10daaed?d=identicon&s=25 M. Edward (Ed) Borasky (Guest)
on 2009-01-12 03:30
(Received via mailing list)
William James wrote:
>> manages to achieve ~50% of the speed of hand-written C. Given that C
>> can be considered portable machine code, this is a pretty impressive
>> achievement.
>
> Don't take Ed seriously.  You will find that he never posts any
> Ruby code that he has written.

Not to the mailing list ... but to RubyForge :)

> And he has already been taught that OCaml can be as fast as C:
>

Of course any language *can* be as fast as any other language. But will
it be *predictably* so for a wide class of benchmarks or applications? I
rather doubt it.


--
M. Edward (Ed) Borasky, FBG, AB, PTA, PGS, MS, MNLP, NST, ACMC(P), WOM

I've never met a happy clam. In fact, most of them were pretty steamed.
F53b05cdbdf561cfe141f69b421244f3?d=identicon&s=25 David A. Black (Guest)
on 2009-01-12 03:37
(Received via mailing list)
Hi --

On Mon, 12 Jan 2009, William James wrote:

>>>
>>> Haskell:
>> }
>> data.map(&mean) # => [2.0, 5.5]
>
> mean = proc{|a| a.inject{|s,x| s+x} / a.size.to_f }
>    ==>#<Proc:0x027949e0@(irb):19>
> data.map &mean
>    ==>[2.0, 5.5]

I believe that's the same as what Joel just posted (give or take some
cosmetics). Or is my eye skipping over something?


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2009-01-12 04:15
(Received via mailing list)
Mike Gold wrote:
>> data.map(&mean) # => [2.0, 5.5]
>
> Sure, you can solve a different problem than the one I gave.
> But the original problem remains.  We cannot, in general, modify
> 'mean'.
>
> Do you propose to change every method in the ruby standard library
> to take just one argument--an array?  That would work.  Sounds like
> Lisp.

Sorry, I may not have followed the discussion very well.

But it seems more natural in this specific case to define mean to take
one array argument, rather than *args, since (or so I imagine), that's
usually how input will arrive. How often do you write code like

   x = ...
   y = ...
   z = ...
   m = mean.call( x,y,z )

as opposed to

   a = ...
   m = mean.call( a )

?

Perhaps I'm distracted by the specifics of mean and missing the general
point...
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-12 04:55
Joel VanderWerf wrote:
> Perhaps I'm distracted by the specifics of mean and missing the general
> point...

'mean' stands for anything.  I should have called it 'some_func'.  You
can't just change its meaning.  In particular, you can't change the
methods in the ruby standard library.  (Well, you can, but you'll be
in danger of receiving a painful retributive wedgie from those who use
your code.  Perhaps even an atomic one.)

You can always write wrappers which convert a given method or lambda
to a one-argument lambda expecting an array.  But this is just another
kind of Lisp emulation in Ruby, taking non-homogeneous primitives and
making them homogeneous.
F53b05cdbdf561cfe141f69b421244f3?d=identicon&s=25 David A. Black (Guest)
on 2009-01-12 05:00
(Received via mailing list)
Hi --

On Mon, 12 Jan 2009, Mike Gold wrote:

> You can always write wrappers which convert a given method or lambda
> to a one-argument lambda expecting an array.  But this is just another
> kind of Lisp emulation in Ruby, taking non-homogeneous primitives and
> making them homogeneous.

Can you give an example of the kind of meaning-changing you mean --
that is, what you would be doing if you could do it? I'm not quite
getting it.


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-12 08:01
(Received via mailing list)
"William James" <w_a_x_man@yahoo.com> writes:

>>   end)
>>  (x . car)
>> irb(main):040:0> (cons 1,2)
>> true
>>         (break)
>> @cdr=#<Cons:0x7fdfb53b8228 @cdr=nil, @car=3>, @car=2>, @car=1>
>>       end)
>> end)
>> end)
>>
>> (def endp(list)
>>
>>
>> (3 2 1)
>> # message:
>>       (arity = (arity - 1))
>> (def function(designator,arity=(-1))
>> join(" , ")))         (sep = " ")
>>       elsif (arity == 0)
>>
>> irb(main):478:0> (begin
> as it should be when you use Commune Lisp!
>
>>    (cons (funcall fun,(first list)),(mapcar fun,(rest list)))
>> nil
>
>> result along with the parameters.  We process the list item by item
>> end)
>>    (printlist (mapcar (function :smallest,1),(list (list 1),
>
>
> Ruby:

No, you're wrong.  All the above code IS RUBY!

> [[1],[1,1,1,1],[1,2,3,4],[4,3,2,1],[1,2,3,4,3,2,1],[4,3,2,1,2,3,4]].
> map{|a| a.min}
>     ==>[1, 1, 1, 1, 1, 1]
>
> Yes, COBOL Lisp is an ancient, cumbersome, and clunky language.
> Ruby is to it as a transistor is to a vacuum tube.

Wrong, the above code wasn't Lisp, it was Matzacred Lisp, aka Ruby.

Ruby is to a vacuum tube what debris of glass and tungsten filaments
are.
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-12 08:01
(Received via mailing list)
Mike Gold <mike.gold.4433@gmail.com> writes:
> Do you propose to change every method in the ruby standard library
> to take just one argument--an array?  That would work.  Sounds like
> Lisp.

Lisp is not like that.
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-12 12:51
Pascal J. Bourguignon wrote:
> Mike Gold <mike.gold.4433@gmail.com> writes:
>> Do you propose to change every method in the ruby standard library
>> to take just one argument--an array?  That would work.  Sounds like
>> Lisp.
>
> Lisp is not like that.

I had hoped it was a given that nothing can be done to actually make
Ruby like Lisp.

In the same spirit of how you fiddled around with Ruby until mapcar sort
of worked, another route is to remove the notion of the function
argument list, replacing it with an array, so that the examples I gave
worked.  In both cases we are artificially removing syntactical
differences, and in this sense it is closer to Lisp.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-12 17:05
Mike Gold wrote:
> Brian Candler wrote:
>> (2) to understand Mike's assertion
>> that Ruby lambdas are not first-class functions (i.e. "The difference
>> between lambda { } and a real first-class function is quite profound").
>
> Ruby:
>
>   mean = lambda { |*args|
>     (args.inject(0) { |acc, x| acc + x })/args.size.to_f
>   }
>   data = [[1,2,3], [4,5,6,7]]
>
>   data.map(&mean) # fail
>   data.map(&:mean) # fail
>   data.map { |*numbers| mean.call(*numbers) } # fail
>   data.map { |numbers| mean.call(*numbers) } # phew, found it

OK, I think I see what you're getting at. You've written a lambda with a
splat (i.e. which takes a variable number of arguments), but needed a
splat at the caller side too. Otherwise the lambda receives a single
argument, which is an Array object.

You could have written the lambda to take a single Array argument:

  mean = lambda { |arg|  arg.inject(0) ...etc }

I don't know enough about Lisp to say how Lisp resolves the same
dichotomy, but surely it must exist. That is, there must be a difference
between calling a function foo with a (list of) n arguments, and calling
a function foo with 1 argument which is itself a list?
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-12 17:31
Mike Gold wrote:
> Can you give an example of where the difference is "very useful"?

Well, as you know, blocks are roughly just in-line lambdas:

  10.times { |x| puts x }

I was thinking of the following properties of the method/lambda
difference:

(1) You can return directly from a method, even from inside blocks.

  class Foo
    def bar
      10.times { |x| ...
        10.times { |y| ...
          return "foo" if baz(x,y)
        }
      }
    end
  end

(2) The whole variable/method naming thing only works because a method
starts a new scope, but a block doesn't.

That is: when I write an expression like 'a+1' I don't have to mark
whether 'a' is a call to a method, or retrieving the value from a local
variable. Neither do I need to declare a to be a variable or a method. I
just use it, and a simple rule says whether what it is, from the
context.

Finally: I just happen to like infix expressions, maybe because these
are how we think in real life, and what we're all taught in school from
an early age. In these, a+1 and (a+1) and ((a+1)) are the same
expression.

In Lisp, it makes a huge semantic difference whether I write
parentheses, and how many, because the Lisp brackets are also in effect
the apply-function operator: like a() in C. So I have to constantly
think about whether I should write 'a' or '(a)' or '(quote a)' or
whatever.

Again, this is saying more about my mind, and possibly my upbringing,
than anything else. My mind is not a Lisp processor.

> Because programmers can and should always be refactoring and changing
> their mind, this entails the drudgework of adding/removing the
> '.call()'s or the '[]'s or (in ruby 1.9) the '.()'s.  The worst
> outcome is when a programmer doesn't bother to refactor because of the
> hassle.

It's not a problem I've ever come across in practice. For me,
refactoring usually means moving code between methods in a class, or
between methods in one class and another class. Usually, code which
expects a block always expects a block.

> By the way, since locals are determined at parse time, I don't see any
> particular reason why this cannot work:
>
>   bisect = lambda { |x, y| (x + y)/2.0 }
>   # ...
>   bisect(5, 6)
>
> Whether or not that _should_ work is a different story.  The pro is
> that I can swap lambdas and methods without changing syntax.  The con
> is, at least, that the meaning of existing code could change (when a
> bisect method already exists).

Sure. In practice, Ruby programs aren't usually composed like that,
since local variables are considered "short lived" (they only exist
during executing of a single method). In some cases I might write

  class Foo
    BISECT = lambda { |x,y| (x+y)/2.0) }

    def foo
      bar(someargs, &BISECT)
    end
  end

but in most cases, the logical place to put the 'lambda' is inline as a
block:

  class Foo
    def foo
      bar(someargs) { |x,y| (x+y)/2.0) }
    end
  end

If 'bisect' were are large piece of code then I might implement this as
a separate method, or even a class.

I think I can see advantages of the "keep it simple" approach. If there
are no choices to make between different ways of expressing the same
thing, then you can just go ahead and code it without thinking. But
then, people have also said here that Lisp has a comprehensive object
system, which in turn means you'd still have to choose between functions
and objects with methods.
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-12 22:20
(Received via mailing list)
Brian Candler <b.candler@pobox.com> writes:

>>   }
> argument, which is an Array object.
>
> You could have written the lambda to take a single Array argument:
>
>   mean = lambda { |arg|  arg.inject(0) ...etc }
>
> I don't know enough about Lisp to say how Lisp resolves the same
> dichotomy, but surely it must exist. That is, there must be a difference
> between calling a function foo with a (list of) n arguments, and calling
> a function foo with 1 argument which is itself a list?

At the function definition site, it's the same, you can decide whether
you'll take a single argument of type list, or several arguments that
will be treated like a list:

(defun unary-function (arguments)
   (process arguments))

(defun variadic-function (&rest arguments)
   (process arguments))

At the call site:

If  you have a list:                               several arguments:
        list-of-args                                    1 2 3

(unary-function list-of-args)                      (unary-function (list
1 2 3))
                                                      [x]
(apply (function variadic-function) list-of-args)  (variadic-function 1
2 3)
  [y]


x: you can always build a list with the variadic function LIST.

y: there's a maximal number of arguments (implementation defined) that
   can be passed to a function, therefore a limit on the length of the
list
   that can be passed with APPLY.



Moreover, Common Lisp as much richer parameter specifications.
You can specify of course mandatory parameters, but in addition:
- optional parameters,
- keyword parameters,
- rest parameters as above.

For both optional parameters and keyword parameters (which are also
optional), you can specify a default value.

For example:

(defun example (mandatory-1 mandatory-2
                 &optional optional-1
                           (optional-2 'default-value
optional-2-present)
                 &rest remaining-arguments-\(including-keys\)
                 &key key-1
                      (key-2 'default-value-2 key-2-present)
                      ((:actual-key key-3) 'default-value-3
key-3-present)
                 &allow-other-keys)

   (print mandatory-1)
   (print mandatory-2)
   (when optional-1          (print optional-1))
   (when optional-2-present  (print optional-2))
   (print remaining-arguments-\(including-keys\))
   (print key-1)
   (when key-2-present (print key-2))
   (when key-3-present (print key-3))
   (values))

C/USER[20]> (example 'm1 'm2 'o1 'o2 :actual-key 'ak :key-1 'k1
:whatever 'you-want)

M1
M2
O1
O2
(:ACTUAL-KEY AK :KEY-1 K1 :WHATEVER YOU-WANT)
K1
AK



(In the case of macros, you can even specify recursive lists, to
deconstruct any syntax).
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-13 04:08
Brian Candler wrote:
> Mike Gold wrote:
>> Brian Candler wrote:
>>>
>>>Yes, a variable holding a lambda is a different beast to a method. As
>>>you said, methods are associated with objects, and lambdas are not.
>>>Usually one suits the usage case better than the other.
>>>
>>>I find the distinct behaviours of both to be very useful in practice.
>>
>> Can you give an example of where the difference is "very useful"?
>
> Well, as you know, blocks are roughly just in-line lambdas:
>
>   10.times { |x| puts x }
>

If by "roughly" you mean "not", then yes.  Blocks are not lambdas, they
are Procs.

> I was thinking of the following properties of the method/lambda
> difference:
>
> (1) You can return directly from a method, even from inside blocks.
>
>   class Foo
>     def bar
>       10.times { |x| ...
>         10.times { |y| ...
>           return "foo" if baz(x,y)
>         }
>       }
>     end
>   end

Lambdas do not abort the enclosing method with 'return'.

def lambda_test
  func = lambda {
    return
  }
  puts "before"
  func.call
  puts "after"
end

def proc_test
  func = Proc.new {
    return
  }

  puts "before"
  func.call
  puts "after"
end

lambda_test # => before
            #    after

proc_test   # => before

The topic was about the problems arising from the syntactic difference
between methods and lambdas.  You've changed gears to a generic
discussion about procs.  My question is yet unaddressed: why is
func.call() or func[] or func.() "very useful" compared to func()?  As I
mentioned, in every case I've found it to be a hassle, for the reasons
previously stated.
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-13 05:23
Brian Candler wrote:
>
> You could have written the lambda to take a single Array argument:
>

This is another repeat of previous comments by others in this thread.
I'll say for at least the third time now: the point is that the lambda
is given -- you cannot change it.

Since the requisite attention has waned, it may be time to give up.  But
I will try one more approach.

The idea of functional programming is to use higher-order functions to
get the job done.  It's like using The Force.  You aren't allowed to
pick up the rock.  You have to concentrate, reach out with The Force,
and make it rise into the air without touching it.  You can only use
higher-order functions.

Initially, you may not be very productive compared to others who are
using their hands to move rocks.  Eventually, however, you are able to
move 100 rocks at once, far surpassing your manual-labor counterparts.

The analogy is exaggerated, but it serves to convey the idea.

Say we wish to add these pairs together,

  data = [[1,2], [3,4], [5, 6]]

Here is the manual approach -- grabbing the rock with your hands:

  data.map { |x, y| x + y }  #=> [3, 7, 11]

The only problem is that it's very specific to this one case.  We've
used 'map', which is a heck of a lot better than not using it, but the
code could still be more general.

Now let's try using The Force.  First we need some helper functions.

  def splatter(func)
    lambda { |args| func.call(*args) }
  end

  def binder(method)
    lambda { |*args| method.bind(args[0]).call(*args[1..-1]) }
  end

All right, here we go:

  data.map(&splatter(binder(Fixnum.instance_method(:+))))
    #=> [3, 7, 11]

Note how general this is.  I can use any method from anywhere (not just
from Fixnum), with any set of data.  This even works with the 'mean'
function which failed before,

  mean = lambda { |*args|
    (args.inject(0) { |acc, x| acc + x })/args.size.to_f
  }
  [[1,2,3], [4,5,6,7]].map(&splatter(mean))
    #=> [2.0, 5.5]

So a functional language is one tailed for programming in this manner:
using higher-order functions, reaching out with The Force.  It is
awkward in Ruby, but not in functional languages which were designed for
it.

Unfortunately the only way to really understand what I mean is to
practice programming in a functional language.  The basics of Haskell
can be learned pretty quickly, I think.  I thought this was a fun book:
http://learnyouahaskell.com .
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 Gary Wright (Guest)
on 2009-01-13 06:08
(Received via mailing list)
On Jan 12, 2009, at 11:22 PM, Mike Gold wrote:
> So a functional language is one tailed for programming in this manner:
> using higher-order functions, reaching out with The Force.  It is
> awkward in Ruby, but not in functional languages which were designed
> for
> it.

And I suspect that programming in an object oriented manner is probably
awkward in a language designed for the functional approach.

I think it is worthwhile understanding different programming styles but
working against the grain of a language seems like a bad idea to me.

Your specific suggestion that 'name()' could be syntactic sugar for
Proc#call introduces an ambiguity in the language that I find
disconcerting.

  a = 0
  b = Proc.new { rand }

  a    # variable reference
  a() # method invocation (self.a)

  b        # variable reference
         b.call  # block invocation via Proc#call
         b()     #  method invocation (self.b)? or disguised Proc#call?


Gary Wright
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-13 07:38
Gary Wright wrote:
> I think it is worthwhile understanding different programming styles but
> working against the grain of a language seems like a bad idea to me.

Yet I have found small-scale use of functional concepts to be useful in
ruby, what I have (perhaps confusingly) called functional-style-in-ruby
(but not functional programming per se): liberal use of map, inject,
etc; avoiding unnecessary re-assignments to the same variable; avoiding
gratuitous side-effects; not caring about temporary objects (until an
efficiency concern is proven).  The same applies to programming in
general.

> Your specific suggestion that 'name()' could be syntactic sugar for
> Proc#call introduces an ambiguity in the language that I find
> disconcerting.

There is no ambiguity because, as I mentioned, locals are determined at
parse time.  However notice I recanted the suggestion seven minutes
later in the post which immediately followed, for similar reasons.

I suspect that when a thread reaches a certain size, nobody actually
reads it.
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-13 07:55
(Received via mailing list)
Mike Gold <mike.gold.4433@gmail.com> writes:

> The topic was about the problems arising from the syntactic difference
> between methods and lambdas.  You've changed gears to a generic
> discussion about procs.  My question is yet unaddressed: why is
> func.call() or func[] or func.() "very useful" compared to func()?  As I
> mentioned, in every case I've found it to be a hassle, for the reasons
> previously stated.

The reason is because Ruby is a lisp-2.  The same name can be used to
designate both a method and a variable.  So you need two syntaxes, to
make reference to the method of a name, or to the variable.  When the
variable contains normal values,

(def f
   :function
end)

f = (lambda { :variable })


irb(main):064:0> [ f , (f []) , (method :f) , (f ()) ]
[ f , (f []) , (method :f) , (f ()) ]
[#<Proc:0x00306ed4@(irb):51>, :variable, #<Method: Object#f>, :function]



In Common Lisp, which is a lisp-2 too, we use FUNCALL to call functions
stored in variables:

(defun f () :function)
(defvar f (lambda () :variable))

C/USER[24]> (list f (funcall f) (function f) (f))
(#<FUNCTION :LAMBDA NIL> :VARIABLE #<FUNCTION F NIL> :FUNCTION)
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-13 10:47
Mike Gold wrote:
> Yet I have found small-scale use of functional concepts to be useful in
> ruby, what I have (perhaps confusingly) called functional-style-in-ruby
> (but not functional programming per se): liberal use of map, inject,
> etc; avoiding unnecessary re-assignments to the same variable; avoiding
> gratuitous side-effects; not caring about temporary objects (until an
> efficiency concern is proven).  The same applies to programming in
> general.

I don't think you'll find any disagreement there. One of the things I
like about Ruby is that it's very easy to pick up in an imperative
style, and then one by one, as you "get" each new concept, you can start
incorporating them in your programs. (for -> each -> map -> inject ->
...etc)

Ruby makes a nice playground for exploring functional concepts (I
finally "got" the Y combinator after converting it into Ruby), but the
lack of tail recursion and the like means that it is not an efficient
environment for large scale use of them. Mapping the problem domain into
classes and methods works much better.

However, we can probably agree that Ruby does make small imperative
programs very easy to write. For example, a basic imperative construct
is "do x, then do y, then do z". In Ruby I just write

    x
    y
    z

In LISP, "do x" is "(x)", but I couldn't write

    (x)
    (y)
    (z)

or even

    (
      (x)
      (y)
      (z)
    )

which will most likely bomb out horribly at runtime (**), but

    (begin
      (x)
      (y)
      (z)
    )

That's a special form and a lot of brackets to do something "simple".

But on the other hand, for someone coming from a functional background,
they might say that the whole concept of "do x, then do y" is rarely if
ever needed. Why would you evaluate something, only to throw it away?

Different languages suit different people's thought processes.

Regards,

Brian.

(**) I believe this is roughly equivalent to: x.call(y,z)
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-13 21:33
Pascal J. Bourguignon wrote:
> Mike Gold <mike.gold.4433@gmail.com> writes:
>
>> Why is func.call() or func[] or func.() "very useful" compared to
>> func()?  As I mentioned, in every case I've found it to be a
>> hassle, for the reasons previously stated.
>
> The reason is because Ruby is a lisp-2.  The same name can be used to
> designate both a method and a variable.  So you need two syntaxes, to
> make reference to the method of a name, or to the variable.

No, that's not the reason.  It is because Ruby allows method invocation
without parens.  Therefore parens cannot be used to unambiguously
"dereference" a function.  The case in point is Python,

class X:
  def f(self):
    return "foo"

x = X()
g = x.f
g() #=> "foo"

triple = lambda z: 3*z
triple(2) #=> 6

We all know the Ruby version, but just for good measure --

class X
  def f
    "foo"
  end
end

x = X.new
g = x.method(:f)
g.call #=> "foo"

triple = lambda { |z| 3*z }
triple.call(2) #=> 6
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-13 21:59
Brian Candler wrote:
> Different languages suit different people's thought processes.

But the different languages shape the thought processes.

A programmer should have at least some experience using a functional
language (Haskell, ML, OCaml, or similar) and a logic language
(Prolog).  (Most everyone is already familiar with object-oriented and
imperative languages.)

The danger of staying in the dark is not so much that you'll miss out
on the language, but that you'll miss out on the thought processes.

If Matz hadn't been a Lisp programmer when he wrote Ruby, I guarantee
that Ruby would have turned out worse.
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 Gary Wright (Guest)
on 2009-01-14 00:10
(Received via mailing list)
On Jan 13, 2009, at 1:37 AM, Mike Gold wrote:
>> Your specific suggestion that 'name()' could be syntactic sugar for
>> Proc#call introduces an ambiguity in the language that I find
>> disconcerting.
>
> There is no ambiguity because, as I mentioned, locals are determined
> at
> parse time.  However notice I recanted the suggestion seven minutes
> later in the post which immediately followed, for similar reasons.

I should have quoted it, but I was responding to your very-late-in-the-
conversation
comment:

> The topic was about the problems arising from the syntactic difference
> between methods and lambdas.  You've changed gears to a generic
> discussion about procs.  My question is yet unaddressed: why is
> func.call() or func[] or func.() "very useful" compared to func()?
> As I
> mentioned, in every case I've found it to be a hassle, for the reasons
> previously stated.

Which I interpreted as support for the name() syntax for function
application.

Presumably you would want function application to be available to any
expression though, not just to a local variable, and so I think you
are still stuck with
the fact that parens can't be unambiguously interpreted during
parsing.  You would
need some precedence rule.

a = object.generate_lambda
a()  # apply, because parser knows that a is a variable not a method
call

object.generate_lambda()     # call, then apply  or just a zero
argument call?
(object.generate_lambda)()  # call, then apply

I'm not arguing that it isn't possible to come up with some rules that
would make it possible
for method/function application to be done via parens.  It just seems
like an unnecessary
complication.

Gary Wright
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-14 02:15
Gary Wright wrote:
> Which I interpreted as support for the name() syntax for function
> application.

I pointed you to the post where I called the idea silly.  Are you
looking for a more dramatic act of contrition?
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-14 10:41
Mike Gold wrote:
> Brian Candler wrote:
>> Different languages suit different people's thought processes.
>
> But the different languages shape the thought processes.

Of course - and dabbling with Lisp and Erlang have undoubtedly been
useful. That doesn't mean I want to abandon Ruby and change to Lisp.

That would be a big investment, for example:

- analysing and choosing which dialect and implementation of Lisp to use
- learning a whole new set of standard libraries
- learning a whole new set of tools and frameworks (e.g. unit testing)
- probably even learning a new editor too

and I am not convinced there would be significant payback in either
productivity or "fun".

OTOH, I can certainly see applications where it would be worthwhile
moving to Erlang - using the CSP model for distributed processing. Of
course, I expect now to be told that Lisp has such capabilities as well
:-)
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-14 11:45
(Received via mailing list)
Mike Gold <mike.gold.4433@gmail.com> writes:

>
> No, that's not the reason.  It is because Ruby allows method invocation
> without parens.  Therefore parens cannot be used to unambiguously
> "dereference" a function.

It wouldn't matter if we had not to distinguish calling a named
function from calling a function stored in a variable.

So we could say that we're in violent agreement.
F53b05cdbdf561cfe141f69b421244f3?d=identicon&s=25 David A. Black (Guest)
on 2009-01-14 14:02
(Received via mailing list)
Hi --

On Wed, 14 Jan 2009, Pascal J. Bourguignon wrote:

>>> designate both a method and a variable.  So you need two syntaxes, to
>>> make reference to the method of a name, or to the variable.
>>
>> No, that's not the reason.  It is because Ruby allows method invocation
>> without parens.  Therefore parens cannot be used to unambiguously
>> "dereference" a function.
>
> It wouldn't matter if we had not to distinguish calling a named
> function from calling a function stored in a variable.

But what would this be:

   f = lambda {}
   def f; end
   f()

I don't think you can express all of Ruby's object model and
assignment semantics if you remove the message-sending semantics from
Proc objects. That's the problem: not when it's one or the other, but
when it's both. (I'm not a big fan of the magical dereferencing of
Procs, I admit, but I'm not sure how it would work even if it were
done.)


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-14 15:11
(Received via mailing list)
"David A. Black" <dblack@rubypal.com> writes:

>>>>> func()?  As I mentioned, in every case I've found it to be a
>> It wouldn't matter if we had not to distinguish calling a named
>> function from calling a function stored in a variable.
>
> But what would this be:
>
>    f = lambda {}
>    def f; end
>    f()

It's clear, f() calls the def f.
You need and have another expression to call the lambda.

And this is because there are two things that are designated by the
identifier f here:

- A function defined with def.
- A variable assigned with =.

Or, said more concisely, Ruby is a lisp-2.
F53b05cdbdf561cfe141f69b421244f3?d=identicon&s=25 David A. Black (Guest)
on 2009-01-14 15:14
(Received via mailing list)
Hi --

On Wed, 14 Jan 2009, Pascal J. Bourguignon wrote:

>>>>>
>>>> "dereference" a function.
> It's clear, f() calls the def f.
> You need and have another expression to call the lambda.
>
> And this is because there are two things that are designated by the identifier f here:

Right -- that's my point :-) I thought that you were suggesting the
unification of calling a method and calling a function that's stored
in a variable.


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 Gary Wright (Guest)
on 2009-01-14 17:40
(Received via mailing list)
On Jan 14, 2009, at 9:09 AM, Pascal J. Bourguignon wrote:

> "David A. Black" <dblack@rubypal.com> writes:
>> But what would this be:
>>
>>   f = lambda {}
>>   def f; end
>>   f()
>
> It's clear, f() calls the def f.
> You need and have another expression to call the lambda.


Maybe something like:

  (f())()

But, like David, I still don't see the net benefit to the language
involved in this line of thinking.

  f[]

Seems to meet most needs without trying to make Ruby something
that it isn't.

Gary Wright
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-14 20:01
(Received via mailing list)
"David A. Black" <dblack@rubypal.com> writes:
>> And this is because there are two things that are designated by the identifier f here:
>
> Right -- that's my point :-) I thought that you were suggesting the
> unification of calling a method and calling a function that's stored
> in a variable.

No such suggestion.  It's just a classification. You have languages
with a single name space for values and functions, and you have
languages with distinct namespaces.  Scheme is in the former.  Ruby,
emacs lisp, Common Lisp are in the later.
1b30afec8cd3efa4c7a10980c10315d8?d=identicon&s=25 Haris Bogdanovic (Guest)
on 2009-01-15 18:29
(Received via mailing list)
I started to learn Common Lisp and I saw that it is a great language.
You have interpreter but you can also compile code. On list of projects
made
by CL is even an operating system. That's impossible with Ruby and
interpreted languages in general. You have functional language which is
also
pure object oriented like Smalltalk or Ruby. Haskell is pure functional
language and I don't see how programmers can manage large portions of
code
without objects. It's like procedural language with functions instead of
procedures.
And also CL has a lot of other advantages. As I understood Pascal, you
can
make code that makes other code and you have support for logic
programming
like Prolog.



"Haris Bogdanovic" <fbogdanovic@xnet.hr> wrote in message
news:gk3m77$1ft$1@gregory.bnet.hr...
9e2504e0b74e5384af09ce8a660afac4?d=identicon&s=25 Pascal J. Bourguignon (Guest)
on 2009-01-15 23:10
(Received via mailing list)
"Haris Bogdanovic" <fbogdanovic@xnet.hr> writes:

> I started to learn Common Lisp and I saw that it is a great language.

Good, go on! :-)

> [...] Haskell is pure functional
> language and I don't see how programmers can manage large portions of code
> without objects. It's like procedural language with functions instead of
> procedures.

First, in sane programming languages, you have the notion of closure:
an anonymous function catches its free variables and encloses them.  A
function is no more a dead function, it's both a function and some
data enclosed in the "closure".  And what an object is?  Some data
enclosed (encapsuled) along with some methods, that is functions.
That's exactly the same.

You can implement closures with objects, and you can implement objects
with closures.

Since Haskell is a functional programming language that has closures
(it wouldn't work otherwise), you can easily implement an object
system, and build big software in Haskell.


However, in PURE functional programming language, where there is no
assignment, there's no state so to speak, since you cannot change it.
So it's hardly worthwhile to make an object.  You couldn't write a
purely functional method to do anything to such an object.  You could
only write methods that would build new objects, all objects being
immutable.

But most functional programming languages introduce some impurities,
so it's usually quite usable.  See for example Ocaml, which is
Objective Categorical Abstract Meta Language, a functional
programming language with objects, modules, pattern matching,
polymorphism, strongly typed, type inference, statically typed, etc.



Ok, here is how you could implement an object with Ruby closures:
(get the missing functions from my previous recent posts).


(def first(list)
  (car list)
end)
(def second(list)
   (car (cdr list))
end)
(def third(list)
  (car (cdr (cdr list)))
end)
(def fourth(list)
  (car (cdr (cdr (cdr list))))
end)
(def fifth(list)
  (car (cdr (cdr (cdr (cdr list)))))
end)

(def assoc(key,alist)
   (if (null alist)
      nil
    elsif (key == (car (car alist)))
      (car alist)
    else
      (assoc key,(cdr alist))
    end)
 end)

(o = (begin
        (a = 1)
        (b = 2)
        (methods = (list (list :getA,(lambda { a })),
                    (list :setA,(lambda { | newA | (a = newA) })),
                    (list :getB,(lambda { b })),
                    (list :setB,(lambda { | newB | (b = newB) })),
                    (list :doSomething,(lambda { | x | (a = (a - b)) ;
(b = x) }))))
        (lambda { | message , *args |
           (method = (assoc message,methods))
           (if (method == nil)
              (throw "No such method "+(message . to_s))
            else
              (funcall (second method),*args)
            end)
         })
      end))


irb(main):146:0> (funcall o,:getA)
(funcall o,:getA)
1
irb(main):147:0> (funcall o,:getB)
(funcall o,:getB)
2
irb(main):148:0> (funcall o,:setA,42)
(funcall o,:setA,42)
42
irb(main):149:0> (funcall o,:doSomething,3)
(funcall o,:doSomething,3)
3
irb(main):150:0> (funcall o,:getA)
(funcall o,:getA)
40
irb(main):151:0> (funcall o,:getB)
(funcall o,:getB)
3
irb(main):152:0>

You can also rename funcall as send, if you want to get a more "oo"
feeling: (send o,:getB)
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-01-16 00:21
Pascal J. Bourguignon wrote:
> Ok, here is how you could implement an object with Ruby closures:

define_method lets you implement objects using locals bound to a
closure,

object = Class.new {
  data = 0
  define_method(:data) {
    data += 1
  }
}.new

p object.data  #=> 1
p object.data  #=> 2
This topic is locked and can not be replied to.