functional programming

  • Thread starter Haris Bogdanovic
  • Start date
P

Pascal J. Bourguignon

Haris Bogdanovic said:
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)).
 
P

Pascal J. Bourguignon

andrea said:
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?

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

Brian Candler

Pascal said:
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.
 
M

Mike Gold

Brian said:
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/browse_thread/thread/472e714cffa0f50c).
 
B

Brian Candler

Mike said:
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?
 
M

Mike Gold

Brian said:
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.
 
M

Martin DeMello

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/browse_thread/thread/472e714cffa0f50c).

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
 
B

Brian Candler

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?
 
H

Haris Bogdanovic

So you want to say that common lisp is pure object oriented like ruby ?.

Pascal J. Bourguignon said:
Haris Bogdanovic said:
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)).
 
R

Robert Klemme

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.

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
 
P

Pascal J. Bourguignon

Brian Candler said:
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.

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

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).
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]
}

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>
# (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?

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

Pascal J. Bourguignon

Martin DeMello said:
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.

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

Pascal J. Bourguignon

Haris Bogdanovic said:
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).
 
B

Brian Candler

Pascal said:
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)
 
P

Pascal J. Bourguignon

Brian Candler said:
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) ).
 
B

Brian Candler

Pascal said:
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.
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 :)
 
P

Pascal J. Bourguignon

Brian Candler said:
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)))

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


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.

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

Pit Capitain

2009/1/10 Pascal J. Bourguignon said:
In Lisp, it can be both, and you always know which you refer.

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

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

Pascal J. Bourguignon

Pit Capitain said:
2009/1/10 Pascal J. Bourguignon said:
In Lisp, it can be both, and you always know which you refer.

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

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

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,218
Latest member
JolieDenha

Latest Threads

Top