Are functions/methods "first class objects"?

sorry this is a bit philosophical, but i just wonder whether ruby can be
considered a language that treats functions and objects as “first
class”? considering you can’t pass a reference-to-a-method as an
argument to another function/method/block in a straightforward manner.

···


dave

David Garamond davegaramond@icqmail.com writes:

sorry this is a bit philosophical, but i just wonder whether ruby can
be considered a language that treats functions and objects as “first
class”? considering you can’t pass a reference-to-a-method as an
argument to another function/method/block in a straightforward manner.

No, I don’t believe it can be considered such a language.

However, generating a bound or unbound method object is only a single
method call away, so it isn’t particularly difficult to work with
method objects if you need to.

Cheers

Dave

David Garamond wrote:

sorry this is a bit philosophical, but i just wonder whether ruby can be
considered a language that treats functions and objects as “first
class”? considering you can’t pass a reference-to-a-method as an
argument to another function/method/block in a straightforward manner.


dave

I guess, I understand your question. In fact, Ruby is,
strictly speaking, no functional language.

BUT:

I looked at some different functioanl and equational languages in
the last years: OCAML,Haskell,Q. I have written some small test
programs in them, and I came to the conclusion that Ruby’s blocks,
proc objects and bindings offers you the same advantages and
flexibility as the “functionality” of those languages. In fact,
I think that Ruby’s syntax allows you to do the same tricks more
intuitively. The only thing which is less elegant in Ruby is the
usage of partial functions.

E.g.:

You could write in OCAML:

Hashtbl.iter (Printf.printf “%s %d”) t;;

In Ruby, you have to write:

t.each { |k,v| printf “%s %d”,[k,v] }

(Note, that you had explicitely specify the variables
k and v (key and value), whereas in OCAML,
(Printf.printf “%s %d”) is a function in two variables.)

But I think this is quite a minor issue: for me, using
explicite arguments simply makes my code more readible while using
blocks allows for the same flexibility as the ML notation
(Writing the iterated code where it belongs to). For me,
omitting arguments to construct function objects is mainly a hack:
E.g. you can’t omit the second first argument
in the CAML notation, so it only works under special cirumstances.
Example: In OCAML, if you print function needs some parameter
coming from outside, here: “postfix”, than you also have to write:

Hashtbl.iter (fun k v → Printf.printf “%s %d %s” k v postfix) t;;

which resembles the Ruby way of specifying the arguments explicitely.

Ruby’s way of allowing for naming pieces of codes
is more elegant and intuitive than the lambda calculus
while being equally powerful. If I’d be cynical, I would say :
this is “functional programming for dummies”. In fact this is
another way of looking with the same results.

Ruby allows for defining and handling of partial functions at least as
easily than the functional languages.

E.g.:

l = lambda { |x,y| f(“specialized”,x,“me too”,y) }

(Compared to the CAML syntax: let l = (fun x y → f “specialized” x “me too” y))

Specializes some arguments of the f functions easily.
Of course, you must remember to use brackets to pass the
arguments to the proc object, but this is quite a
minor issue (and has some advantages also).

Using blocks has some other benevolent psychological
and aesthetical aspect: it prevents an inflation of parenthesis.
Whatever you prefer: do … end or
{ … }, it gives valuable hints: this is a piece
of executable code, while the CAML, LISP and smalltalk
syntax is much less balanced in my eyes.

Therefore, I think that Ruby is at least as elegant
and powerful in handling functional features as the functional languges.

Regards, Christian

“Christian Szegedy”

Using blocks has some other benevolent psychological
and aesthetical aspect: it prevents an inflation of parenthesis.
Whatever you prefer: do … end or
{ … }, it gives valuable hints: this is a piece
of executable code, while the CAML, LISP and smalltalk
syntax is much less balanced in my eyes.

Therefore, I think that Ruby is at least as elegant
and powerful in handling functional features as the functional languges.

Try writing something as seemly simple as a functional
Proc composition in Ruby (preserving arity etc.)

class Proc
def *(an_other_proc)

end
end

This experience might change your mind …

/Christoph

Christoph wrote:

“Christian Szegedy”

Using blocks has some other benevolent psychological
and aesthetical aspect: it prevents an inflation of parenthesis.
Whatever you prefer: do … end or
{ … }, it gives valuable hints: this is a piece
of executable code, while the CAML, LISP and smalltalk
syntax is much less balanced in my eyes.

Therefore, I think that Ruby is at least as elegant
and powerful in handling functional features as the functional languges.

Try writing something as seemly simple as a functional
Proc composition in Ruby (preserving arity etc.)

class Proc
def *(an_other_proc)

end
end

This experience might change your mind …

/Christoph

class Proc
def *(other,*parms)
self[other[*parms)]]
end
end

Christoph wrote:

“Christian Szegedy”

Using blocks has some other benevolent psychological
and aesthetical aspect: it prevents an inflation of parenthesis.
Whatever you prefer: do … end or
{ … }, it gives valuable hints: this is a piece
of executable code, while the CAML, LISP and smalltalk
syntax is much less balanced in my eyes.

Therefore, I think that Ruby is at least as elegant
and powerful in handling functional features as the functional languges.

Try writing something as seemly simple as a functional
Proc composition in Ruby (preserving arity etc.)

class Proc
def *(an_other_proc)

end
end

This experience might change your mind …

/Christoph

Oops, I was (now, It tested it):

class Proc
def *(other)
lambda{ |parms| self[other[*parms]] }
end
end

“Christian Szegedy” szegedy@nospam.or.uni-bonn.de wrote in message
news:3D902512.9060702@nospam.or.uni-bonn.de

Christoph wrote:

“Christian Szegedy”

Using blocks has some other benevolent psychological
and aesthetical aspect: it prevents an inflation of parenthesis.
Whatever you prefer: do … end or
{ … }, it gives valuable hints: this is a piece
of executable code, while the CAML, LISP and smalltalk
syntax is much less balanced in my eyes.

Therefore, I think that Ruby is at least as elegant
and powerful in handling functional features as the functional languges.

Try writing something as seemly simple as a functional
Proc composition in Ruby (preserving arity etc.)

class Proc
def *(an_other_proc)

end
end

This experience might change your mind …

/Christoph

Oops, I was (now, It tested it):

class Proc
def *(other)
lambda{ |parms| self[other[*parms]] }
end
end

That is nice - however

···

class Proc
def *(other)
lambda{ |parms| self[other[*parms]] }
end
end

a = lambda {|x,| x }
b = lambda {|y,| y }

c = b * a
p a.arity, c.arity # 1, -1

Anyway the example I had in mind is
more along the lines of


k = 35
a = lambda {|x,y| [k + 2 * x, k + 3 * x] }
b = lambda {|t,s| t + s }

c = b * c

c[1,1] = = 75 # true

  • this should work with essentially all in and out going
    arities. Of course one can do it but it is more tedious
    then you might think (which was my sole point) .

/Christoph

Christoph wrote:

a = lambda {|x,| x }
b = lambda {|y,| y }

c = b * a
p a.arity, c.arity # 1, -1

OK, I see your point.


k = 35
a = lambda {|x,y| [k + 2 * x, k + 3 * x] }
b = lambda {|t,s| t + s }

c = b * c

c[1,1] = = 75 # true

I don’t understand your example. I guess, you wanted to
write
c = b*a
but what do you expect to be the result of the composition
of a two variable function and a two variable function?
A two variable function returning a one variable function?
But this does not match your example…
Perhaps you wanted to write c[1,1][k] == 75 ?

  • this should work with essentially all in and out going
    arities. Of course one can do it but it is more tedious
    then you might think (which was my sole point) .

OK, I admit that in this generality it is not so simple in
Ruby as in (***)ML.

Now, let us be honest, is it so important? I do a lot of
meta-programming, but I have never encountered a situation
requiring such a high level approach.

Regards, Christian

“Christian Szegedy”

k = 35
a = lambda {|x,y| [k + 2 * x, k + 3 * x] }
b = lambda {|t,s| t + s }

c = b * c

c[1,1] = = 75 # true

I don’t understand your example. I guess, you wanted to
write
c = b*a

Yes - sorry …

but what do you expect to be the result of the composition
of a two variable function and a two variable function?
A two variable function returning a one variable function?
But this does not match your example…
Perhaps you wanted to write c[1,1][k] == 75 ?

No I really mend

c[1,1] == 75 # in a pestrian way

···

k = 35
a = lambda {|x,y| [k + 2 * x, k + 3 * x] }
b = lambda {|t,s| t + s }

c = lambda {|_t,_s| b[*a[_t,_s]] }

p c[1,1] # 75

  • this should work with essentially all in and out going
    arities. Of course one can do it but it is more tedious
    then you might think (which was my sole point) .

OK, I admit that in this generality it is not so simple in
Ruby as in (***)ML.

Now, let us be honest, is it so important? I do a lot of
meta-programming, but I have never encountered a situation
requiring such a high level approach.

I guess this (and other functional deficiencies'') is (are) not important since Ruby is probably more of an functionally
enhanced imperative language’’ - so any potential weakness
of Rubys functional side are well taken care of by its
strong imperative meta features.

/Christoph

Hello Christian,

Tuesday, September 24, 2002, 4:40:31 PM, you wrote:

Now, let us be honest, is it so important? I do a lot of
meta-programming, but I have never encountered a situation
requiring such a high level approach.

i had. sorting a list of filenames on given criterion:

sort_by “es”, filenames # sorts by extension then size
sort_by “n”, filenames # sorts by filename w/o path and extension

i want to create meta-function which returns function to sort (or just
compare criterions) given ordering string:

sort_by “es”
returns
{|filename| [File.ext(filename), File.stat(filename).size] }

can you (and Ruby :slight_smile: help me? -eval

···


Best regards,
Bulat mailto:bulatz@integ.ru

Christoph wrote:

k = 35
a = lambda {|x,y| [k + 2 * x, k + 3 * x] }
b = lambda {|t,s| t + s }

c = lambda {|_t,_s| b[*a[_t,_s]] }

p c[1,1] # 75

I see, thanks.

I think that want a little too much from Ruby.
In OCAML e.g it would not be possible to recognize
tuples as return values and then “curryfy” them in
a general way (i.e handling tuples of any size),
because of the static type safety.)
In Ruby, it would be possible, but requires some
ugly hacking.

Regards, Christian

sort_by “es”, filenames # sorts by extension then size
sort_by “n”, filenames # sorts by filename w/o path and extension

Ruby 1.7 has Array#sort_by. The above are written as:

filenames.sort_by{|i| File.extname(i), File.size(i)]}
filenames.sort_by{|i| File.basename(i).sub(/.[^.]+$/, “”)}

i want to create meta-function which returns function to sort (or just
compare criterions) given ordering string:

sort_by “es”
returns
{|filename| [File.ext(filename), File.stat(filename).size] }

can you (and Ruby :slight_smile: help me? -eval

How about:

def criteria(switch)
case switch
when “es”
Proc.new{|i| [File.extname(i), File.size(i)]}
when “n”
Proc.new{|i| File.basename(i).sub(/.([^.]+)$/, “”)}
else
Proc.new{|i| i}
end
end

puts Dir[“*”].sort_by(&criteria(“es”))

– Gotoken

···

At Wed, 25 Sep 2002 13:53:46 +0900, Bulat Ziganshin wrote:

Bulat,

Your problem is interesting, but I can’t provide much help because your post is
difficult to read:

  • it is an HTML email, whereas code is much much easier to read in plain text,
    and plain text should always be used on newgroups and mailing lists
  • your writing is sloppy: all lower-case letters
  • the description of what you want to achieve is unclear

I am only gently criticising you here, and merely wish to point out that you
will probably get a better response if you pay attention to clarity, detail and
netiquette. Hopefully others can provide greater assistance.

Regarding what you want to achieve, I suggest you concentrate on the objective
(sorting files on multiple criteria) and not on the method (returning function
to sort). You are trying to solve the problem in a functional way whereas I’m
sure it is better solved in Ruby using the OO paradigm.

If there is a pressing need to use the method you describe, I’d like to know
what it is and how I can best advise.

I hope this helps,

Gavin

···

----- Original Message -----
From: “Bulat Ziganshin” bulatz@integ.ru
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Wednesday, September 25, 2002 2:53 PM
Subject: Re: are functions/methods “first class objects”?

Hello Christian,

Tuesday, September 24, 2002, 4:40:31 PM, you wrote:

Now, let us be honest, is it so important? I do a lot of
meta-programming, but I have never encountered a situation
requiring such a high level approach.

i had. sorting a list of filenames on given criterion:

sort_by “es”, filenames # sorts by extension then size
sort_by “n”, filenames # sorts by filename w/o path and extension

i want to create meta-function which returns function to sort (or just
compare criterions) given ordering string:

sort_by “es”
returns
{|filename| [File.ext(filename), File.stat(filename).size] }

can you (and Ruby :slight_smile: help me? -eval


Best regards,
Bulat mailto:bulatz@integ.ru

Correcting a detail of my message:

Bulat, […]

  • it is an HTML email, whereas code is much much easier to read in plain
    text,
    and plain text should always be used on newgroups and mailing lists

[…]

I apologise, it appears your messages are plain text. I don’t know why, but my
(admittedly crap) client displays your messages in proportianal font, whereas
almost everyone else is displayed in fixed-width. Oh well…

Gavin

···

----- Original Message -----
From: “Gavin Sinclair” gsinclair@soyabean.com.au

Hello GOTO,

Wednesday, September 25, 2002, 9:30:40 AM, you wrote:

Ruby 1.7 has Array#sort_by. The above are written as:

i know

def criteria(switch)
case switch
when “es”
Proc.new{|i| [File.extname(i), File.size(i)]}
when “n”
Proc.new{|i| File.basename(i).sub(/.([^.]+)$/, “”)}
else
Proc.new{|i| i}
end
end

puts Dir[“*”].sort_by(&criteria(“es”))

hmm. you don’t understand. sort string is a sequence of chars, which
maps to sort criterions. this char may be

e for extennsion
n for filename w/o path/extension
s for size
g for group (one group is c sources, next - executables and so on)
i for some intellectual THING :slight_smile:

i need to translate sequence of this chars to function which returns
array with all the data for sort_by:

sort_files “es”
sort_files “en”
sort_files “geis”

and much more variants. so i need to construct closure on the fly

···


Best regards,
Bulat mailto:bulatz@integ.ru

hmm. you don’t understand. sort string is a sequence of chars, which
maps to sort criterions. this char may be

oops. I didn’t read context :<

e for extennsion
n for filename w/o path/extension
s for size
g for group (one group is c sources, next - executables and so on)
i for some intellectual THING :slight_smile:

i need to translate sequence of this chars to function which returns
array with all the data for sort_by:

sort_files “es”
sort_files “en”
sort_files “geis”

and much more variants. so i need to construct closure on the fly

I see. A point seems how to pass a filename to each component…

class Proc
def *(other) lambda{|i| other.yield(self.yield(i))} end
end

F_Bootstrap = Proc.new{|i| [i, ]}
F_SIZE = Proc.new{|i,a| [i, a << File.size(i)]}
F_EXT = Proc.new{|i,a| [i, a << File.extname(i)]}
F_Trailer = Proc.new{|i,a| a}

def criteria(opt)
f = F_Bootstrap
opt.each_byte do |c|
case c
when ?s
f *= F_SIZE
when ?e
f *= F_EXT
end
end
f * F_Trailer
end

puts Dir[“*”].sort_by(&criteria(“se”))

– Gotoken

···

At Wed, 25 Sep 2002 15:59:02 +0900, Bulat Ziganshin wrote:

Case statement can be replaced an hash (like Christian’s [ruby-talk:51284]).

class Proc
def *(other) Proc.new{|i| other.yield(self.yield(i))} end
end

CRITERIA_PROCS = {
?a => Proc.new{|i,a| [i, a << File.atime(i)]},
?c => Proc.new{|i,a| [i, a << File.ctime(i)]},
?e => Proc.new{|i,a| [i, a << File.extname(i)]},
?m => Proc.new{|i,a| [i, a << File.mtime(i)]},
?n => Proc.new{|i,a| [i, a << File.basename(i).sub(/\.([^.]+)$/, “”)]},
?s => Proc.new{|i,a| [i, a << File.size(i)]},
# and more
}

def criteria(opt, part = CRITERIA_PROCS)
f = Proc.new{|i| [i, ]}
opt.each_byte{|c| f *= part[c]}
f * Proc.new{|i,a| a}
end

puts Dir[“*”].sort_by(&criteria(“esm”))

– Gotoken

···

At Wed, 25 Sep 2002 19:14:50 +0900, GOTO Kentaro wrote:

def criteria(opt)
f = F_Bootstrap
opt.each_byte do |c|
case c
when ?s
f *= F_SIZE
when ?e
f *= F_EXT
end
end
f * F_Trailer
end