Defining a new function by composition

E

Edgardo Hames

Hi.

I would like to add a method to the Array class, say Array#pop! which
is actually split!(-1). But then, I can think of a more general
problem: define a new function by composition of two or more
functions.
In Haskell, I can do something like

f :: a -> b -> c
f x y = something

g::b -> c
g = f some_value

What is the Ruby equivalent of this?

Regards,
Ed
 
R

Robert Klemme

Edgardo Hames said:
Hi.

I would like to add a method to the Array class, say Array#pop! which
is actually split!(-1). But then, I can think of a more general
problem: define a new function by composition of two or more
functions.
In Haskell, I can do something like

f :: a -> b -> c
f x y = something

g::b -> c
g = f some_value

What is the Ruby equivalent of this?

Regards,
Ed

How about

class Array
def pop!() split!(-1) end
end

Note: pop! and split! are not functions but methods. So you always have an
implicit argument (named 'self' in Ruby). I think this does not lend easily
to chaining the way you seek.

Of course, for the general case you could do something like this:

module Kernel
private
def chain(name, *funcs)
eval "def #{name}(*a) #{funcs.map {|f| "#{f}("}}*a#{")" * funcs.size}
end"
end
end
def foo(x) "<#{x}>" end => nil
def bar(x) "[#{x}]" end => nil
chain :xxx, :foo, :bar => nil
xxx 100
=> "<[100]>"

Or a more functional approach:

module Kernel
private
def chain2(*funcs)
lambda {|*a| funcs.inject(a){|val, fun| send(fun, *val) } }
end
end
=> "<[100]>"

Note the different order.

Regards

robert
 
E

Edgardo Hames

How about

class Array
def pop!() split!(-1) end
end

I imagined this. But, I thought you were going to surprise me with a
weird (or unimagined) way to use alias ;-)

Thanks,
Ed
 
M

mark sparshatt

Edgardo said:
I imagined this. But, I thought you were going to surprise me with a
weird (or unimagined) way to use alias ;-)
Though this won't do what you want since there isn't a method split! for
array.

I think what you actually want is something like

class Array
def pop!
slice!(-1, 1)
end
end

This discussion did give me an idea for a curry method, that let's you
predefine the parameters for a method.

class Class
def curry(newmethod, oldmethod, stored_params)
send:)define_method, newmethod) do |*args|
x = stored_params
x += args if args
send(oldmethod, *x)
end
end
end

then you can define pop! with

class Array
curry:)pop!, :slice!, -1, 1)
end

then
x = [1,2,3]
p x.slice! #=> 3
p x #=> [1,2]
 
E

Edgardo Hames

Though this won't do what you want since there isn't a method split! for
array.

Since there is a method push which modifies the receiver, I believe
pop should do the same. I find it kind of odd this isn't so.

Regards.
Ed
 
C

Carlos

Since there is a method push which modifies the receiver, I believe
pop should do the same. I find it kind of odd this isn't so.

$ ruby -e 'a=[1,2,3]; a.pop; p a'
[1, 2]
 
R

Robert Klemme

Edgardo Hames said:
I imagined this. But, I thought you were going to surprise me with a
weird (or unimagined) way to use alias ;-)

:) Not possible because of the argument.

Here's another solution - even more functional, although I still think this
looks a bit more elegant in functional languages:

def concat3(*fun)
lambda {|*a| fun.inject(a) {|val,f| f.call(*val)} }
end
foo = lambda {|x| "<#{x}>"}
=> # said:
bar = lambda {|x| "[#{x}]"}
=> # said:
xx3 = concat3 bar, foo
=> # said:
xx3.call 100
=> said:
xx3[ 100 ]
=> "<[100]>"

As you can see, there are plenty ways to do this. And you can even use Ruby
as a functional language, although I'd say best use is made of it if you use
it OO.

Regards

robert
 

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,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top