curried and/or closured value dispatch

Steve Yegge <[email protected]> Sun, 6 Mar 2005 15:05:33 -0800
Newsgroups gmane.comp.lang.nice.general
Message-ID <[email protected]>
Greetings,

I'm trying to write a key handler in my NiceSwing app, and here's
the meat of it:

let (int, int) NORTH = (0, -1);
let (int, int) SOUTH = (0,  1);
let (int, int) EAST  = (1,  0);
let (int, int) WEST  = (-1, 0);

void handleKeypress(KeyEvent e) {
    void handleKey(int code) {}

    handleKey(KeyEvent.VK_UP)    = moveOrCharge(e, NORTH);
    handleKey(KeyEvent.VK_8)     = moveOrCharge(e, NORTH);
    handleKey(KeyEvent.VK_K)     = moveOrCharge(e, NORTH);

    handleKey(KeyEvent.VK_DOWN)  = moveOrCharge(e, SOUTH);
    handleKey(KeyEvent.VK_2)     = moveOrCharge(e, SOUTH);
    handleKey(KeyEvent.VK_J)     = moveOrCharge(e, SOUTH);

    handleKey(KeyEvent.VK_RIGHT) = moveOrCharge(e, EAST);
    handleKey(KeyEvent.VK_6)     = moveOrCharge(e, EAST);
    handleKey(KeyEvent.VK_L)     = moveOrCharge(e, EAST);

    handleKey(KeyEvent.VK_LEFT)  = moveOrCharge(e, WEST);
    handleKey(KeyEvent.VK_4)     = moveOrCharge(e, WEST);
    handleKey(KeyEvent.VK_H)     = moveOrCharge(e, WEST);

    handleKey(KeyEvent.VK_N) = nextLevel(currentLevel+1);
    handleKey(KeyEvent.VK_P) = nextLevel(currentLevel-1);
    handleKey(KeyEvent.VK_Q) = saveAndQuit();
    handleKey(KeyEvent.VK_R) = restartLevel();
    handleKey(KeyEvent.VK_U) = undoLastPush();
    handleKey(KeyEvent.VK_W) = playFinale();

    if ( !(currentlyAnimating || playingFinale) ) {
	handleKey(e.getKeyCode());
    }    
}

What I want is to be able to dispatch on the value of the key code,
but pass the actual KeyEvent to the moveOrCharge function.

I've tried all sorts of variations on this code, and none are working.
The one above is an attempt to define them all internally to
handleKeypress(), in the hopes that the sub-methods will close
over the KeyEvent parameter.

Another possibility would be to create curried versions that have
the KeyEvent parameter pre-packaged.

Here's some equivalent Java code to give the flavor of what I'm doing:

	Map<Integer, int[]> mk = new HashMap<Integer, int[]>();
	for (int k : new int[]{VK_UP,    VK_8, VK_K}) mk.put(k, NORTH);
	for (int k : new int[]{VK_DOWN,  VK_2, VK_J}) mk.put(k, SOUTH);
	for (int k : new int[]{VK_RIGHT, VK_6, VK_L}) mk.put(k, EAST);
	for (int k : new int[]{VK_LEFT,  VK_4, VK_H}) mk.put(k, WEST);
	MOVE_KEY_MAP = mk;

    void handleKeypress ( KeyEvent e ) {
	if ( m_currentlyAnimating || m_playingFinale ) return;
	int[] dir = MOVE_KEY_MAP.get(e.getKeyCode());

	if (dir != null) {
	    moveOrCharge (e, dir[0], dir[1]);
	}
	else {
	    switch (e.getKeyCode()) {
	    case VK_N:  nextLevel(m_currentLevel+1); break;
	    case VK_P:  nextLevel(m_currentLevel-1); break;
	    case VK_Q:  saveAndQuit();  	     break;
	    case VK_R:  restartLevel();   	     break;
	    case VK_U:  undoLastPush(); 	     break;
	    case VK_W:  playFinale();	 	     break; // test winning
	    }
	}
    }

First I'd love to know how to get it working, *any* way -- doesn't
have to be value dispatch.  I'd prefer not to have to use a giant
cascading if-statement, though.

Second, I'd love to know how to do it reasonably elegantly.
There's a lot of duplication in my Nice attempt above.  For instance,
is it possible to do value dispatch by saying "call this one for any
of these three particular values?"  Something like:

handleKey([VK_UP, VK_8, VK_K]) = ...

or perhaps:

for (int code : [VK_UP, VK_8, VK_K]) handleKey(code) = ...

Oh, and is it possible to do the equivalent of Java 1.5's new static
import, so I can refer to VK_UP, etc. without prefixing the KeyEvent
classname?  (Not a big deal, just wondering).

Thanks again!

-steve


-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click