Faking continuation based web serving using exceptions
A friend has pointed out to me that the command line and web interface in my last post do not need to interact with the main game through an iterator. He proposed that the web interface could pause the execution of the game by using exceptions. I played with the idea, and discovered that he was right. The upshot is that continuation-based web serving can be faked in any language which has exceptions. (Lexical closures are also helpful, but can also be faked using objects.) The approach given below relies on neither CPS nor monads, and so has the added advantage of being fairly idiomatic in most mainstream languages.
As before, our game is the children’s “Guess a number between 1 and 100” game:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
I like this version much better because the ugly wart of before:
# The coroutine equivalent of self.__play_game()
iter = self.__play_game()
try:
y = yield iter.next()
while True:
y = yield iter.send(y)
except StopIteration:
pass
has been simplified to:
self.__play_game()
The interface between the game and the user interface is the same as before, with one addition:
display(text) # displays the given text to the user and returns None
prompt_string(text) # displays the given text to the user and returns a string input by the user
prompt_int(text) # display the given text to the user and returns an int input by the user
prompt_yes_no(text) # display the given text to the user and returns True for yes and False for no
get(callback) # invokes the callback and returns what it returns
The new method “get” is made use of in the game when generating the answer for the game:
my_number = self.interface.get(lambda: rgen.randint(1,100))
Retrieving the random number through self.interface.get ensures that the game will not be constantly changing its answer while a user is playing through the web interface.
As before, the command line interface is very simple:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
The web interface works by raising a StopWebInterface exception when execution of the game needs to be paused so that the user can input some data into a form. Our abstraction is thus slightly leaky, in that a game which at some point generically caught all types of exceptions might interfere with the behavior of the web interface. The yield lambda solution did not have this problem.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
|