Python Rocks! and other rants 19.5.2004
Weblog of Kent S Johnson

2004-05-19

First-class functions are my friends

Python's first-class functions make life easier in so many ways. A Java programmer will say, "I can do the same thing with interfaces and anonymous inner classes," but it's just not the same.

The most obvious use is for callbacks of any kind. The simplest example is a function that runs a command. In Python, the command can be passed directly into the runner as a function. For example:

# The run function just calls the callback
def run(cmd):
    
cmd()

# A simple command
def myCommand():
    
print 'myCommand'

# Run the command
run(myCommand)  # prints 'myCommand'

You can pass an instance method to runner using a bound method:

class RunnerClient:
    
def useRunner(self):
        
run(self.command)
    
    
def command(self):
        
print 'RunnerClient.command'

RunnerClient().useRunner()  # prints 'RunnerClient.command'

This can easily be extended to pass any number of arguments to the command given to runner, and to return any number of results:

# The run function
def run(cmd, *args):
    
return cmd(*args)

# A simple command
def myCommand(x, y):
    
print 'x = %d, y = %d' % (x, y)
    
return x+10, y+10

# Run the command
z, w = run(myCommand, 10, 20)   # prints 'x = 10, y = 20'
print z, w  # prints '20 30'

Alternatively, the command arguments can be bound to a function of no arguments using lambda:

# The run function
def run(cmd):
    
return cmd()

# A simple command
def myCommand(x, y):
    
print 'x = %d, y = %d' % (x, y)
    
return x+10, y+10

# Run the command
z, w = run(lambda : myCommand(10, 20))

That's easy! What does it look like in Java? Let's start with the simplest case above. We need an interface for the command, a Runner class and an instantiation of the command. I made Command and Runner inner classes to keep everything in one module; normally they would be top-level classes. Here is the code:

public class RunnerClient {
    private interface Command {
        public void execute();
    }

    private static class Runner {
        public static void run(Command cmd) {
            cmd.execute();
        }
    }

    public void useRunner() {
        Command cmd = new Command() {
            public void execute() {
                System.out.println("myCommand");
            }
        };

        Runner.run(cmd);
    }

    public static void main(String[] args) {
        new RunnerClient().useRunner();
    }
}

Yikes! That's nasty. So much noise! There is the visual noise of the extra punctuation and type declarations, and the conceptual noise of class declarations.

If you want the callback to be a member function, it's not too much different. The anonymous inner class can forward to the method of the main class:

public class RunnerClient {
    private interface Command {
        public void execute();
    }

    private static class Runner {
        public static void run(Command cmd) {
            cmd.execute();
        }
    }

    public void useRunner() {
        Command cmd = new Command() {
            public void execute() {
                command();
            }
        };

        Runner.run(cmd);
    }

    public void command() {
        System.out.println("RunnerClient.command()");
    }

    public static void main(String[] args) {
        new RunnerClient().useRunner();
    }
}

To pass arguments to the command...I'm not sure I want to think about that. Probably the simplest solution is to make a specific Command implementation to hold the arguments to the command. You need a different helper class for each command signature. Maybe there is a way to do it with introspection. Any way you cut it, it's going to be painful.

posted at 09:43:28    #    comment []    trackback []
May 2004
MoTuWeThFrSaSu
      1 2
3 4 5 6 7 8 9
10111213141516
17181920212223
24252627282930
31      
Apr
2004
 Jun
2004

Comments about life, the universe and Python, from the imagination of Kent S Johnson.

XML-Image Letterimage

BlogRoll

© 2004, Kent Johnson