simplified world terminology

svn: r13041
This commit is contained in:
Matthias Felleisen 2009-01-08 23:04:27 +00:00
parent 78ec585827
commit 2821275df7

View File

@ -32,9 +32,9 @@
This @tt{universe.ss} teachpack implements and provides the functionality
for creating interactive, graphical programs that consist of plain
mathematical functions. We refer to such programs as @defterm{world}
mathematical functions. We refer to such programs as @deftech{world}
programs. In addition, world programs can also become a part of a
@defterm{universe}, a collection of worlds that can exchange messages.
@deftech{universe}, a collection of worlds that can exchange messages.
The purpose of this documentation is to give experienced Schemers and HtDP
teachers a concise overview for using the library. The first part of the
@ -42,7 +42,7 @@ The purpose of this documentation is to give experienced Schemers and HtDP
presents an illustration of how to design such programs for a simple
domain; it is suited for a novice who knows how to design conditional
functions for symbols. The second half of the documentation focuses on
@tech{universe} programs: how it is managed via a server, how @tech{world}
"universe" programs: how it is managed via a server, how @tech{world}
programs register with the server, etc. The last two sections show how to
design a simple universe of two communicating worlds.
@ -138,17 +138,17 @@ The following picture provides an intuitive overview of the workings of a
@image["nuworld.png"]
The @scheme[big-bang] form installs @scheme[World_0] as the initial
world. The handlers @scheme[tock], @scheme[react], and @scheme[click] transform
The @scheme[big-bang] form installs @scheme[World_0] as the initial @tech{WorldState}.
The handlers @scheme[tock], @scheme[react], and @scheme[click] transform
one world into another one; each time an event is handled, @scheme[done] is
used to check whether the world is final, in which case the program is
shut down; and finally, @scheme[draw] renders each world as a scene, which
is then displayed on an external canvas.
@deftech{World} : @scheme[any/c]
@deftech{WorldState} : @scheme[any/c]
The design of a world program demands that you come up with a data
definition of all possible states. We use @tech{World} to refer to
definition of all possible states. We use @tech{WorldState} to refer to
this collection of data, using a capital W to distinguish it from the
program. In principle, there are no constraints on this data
definition though it mustn't be an instance of the @tech{Package}
@ -176,7 +176,7 @@ The design of a world program demands that you come up with a data
starts a @tech{world} program in the initial state specified with
@scheme[state-expr], which must of course evaluate to an element of
@tech{World}. Its behavior is specified via the handler functions
@tech{WorldState}. Its behavior is specified via the handler functions
designated in the optional @scheme[spec] clauses, especially how the
@tech{world} program deals with clock ticks, with key events, with mouse
events, and eventually with messages from the universe; how it renders
@ -190,7 +190,7 @@ The design of a world program demands that you come up with a data
@item{
@defform[(on-tick tick-expr)
#:contracts
([tick-expr (-> (unsyntax @tech{World}) (unsyntax @tech{World}))])]{
([tick-expr (-> (unsyntax @tech{WorldState}) (unsyntax @tech{WorldState}))])]{
tell DrScheme to call the @scheme[tick-expr] function on the current
world every time the clock ticks. The result of the call becomes the
@ -199,7 +199,7 @@ current world. The clock ticks at the rate of 28 times per second.}}
@item{
@defform/none[(on-tick tick-expr rate-expr)
#:contracts
([tick-expr (-> (unsyntax @tech{World}) (unsyntax @tech{World}))]
([tick-expr (-> (unsyntax @tech{WorldState}) (unsyntax @tech{WorldState}))]
[rate-expr natural-number/c])]{
tell DrScheme to call the @scheme[tick-expr] function on the current
world every time the clock ticks. The result of the call becomes the
@ -234,7 +234,7 @@ A character is used to signal that the user has hit an alphanumeric
@defform[(on-key change-expr)
#:contracts
([change-expr (-> (unsyntax @tech{World}) key-event? (unsyntax @tech{World}))])]{
([change-expr (-> (unsyntax @tech{WorldState}) key-event? (unsyntax @tech{WorldState}))])]{
tell DrScheme to call @scheme[change-expr] function on the current world and a
@tech{KeyEvent} for every keystroke the user of the computer makes. The result
of the call becomes the current world.
@ -288,7 +288,7 @@ All @tech{MouseEvent}s are represented via symbols:
@defform[(on-mouse clack-expr)
#:contracts
([clack-expr
(-> (unsyntax @tech{World}) natural-number/c natural-number/c (unsyntax @tech{MouseEvent}) (unsyntax @tech{World}))])]{
(-> (unsyntax @tech{WorldState}) natural-number/c natural-number/c (unsyntax @tech{MouseEvent}) (unsyntax @tech{WorldState}))])]{
tell DrScheme to call @scheme[clack-expr] on the current world, the current
@scheme[x] and @scheme[y] coordinates of the mouse, and and a
@tech{MouseEvent} for every (noticeable) action of the mouse by the
@ -303,7 +303,7 @@ All @tech{MouseEvent}s are represented via symbols:
@defform[(on-draw render-expr)
#:contracts
([render-expr (-> (unsyntax @tech{World}) scene?)])]{
([render-expr (-> (unsyntax @tech{WorldState}) scene?)])]{
tell DrScheme to call the function @scheme[render-expr] whenever the
canvas must be drawn. The external canvas is usually re-drawn after DrScheme has
@ -312,7 +312,7 @@ All @tech{MouseEvent}s are represented via symbols:
@defform/none[(on-draw render-expr width-expr height-expr)
#:contracts
([render-expr (-> (unsyntax @tech{World}) scene?)]
([render-expr (-> (unsyntax @tech{WorldState}) scene?)]
[width-expr natural-number/c]
[height-expr natural-number/c])]{
@ -325,7 +325,7 @@ All @tech{MouseEvent}s are represented via symbols:
@defform[(stop-when last-world?)
#:contracts
([last-world? (-> (unsyntax @tech{World}) boolean?)])]{
([last-world? (-> (unsyntax @tech{WorldState}) boolean?)])]{
tell DrScheme to call the @scheme[last-world?] function whenever the canvas is
drawn. If this call produces @scheme[true], the world program is shut
down. Specifically, the clock is stopped; no more
@ -436,7 +436,8 @@ it to the locked position; and}
Simulating any dynamic behavior via a @tech{world} program demands two
different activities. First, we must tease out those portions of our
domain that change over time or in reaction to actions, and we must
develop a data representation @deftech{D} for this information. Keep in
develop a data representation for this information. This is what we call
@tech{WorldState}. Keep in
mind that a good data definition makes it easy for readers to map data to
information in the real world and vice versa. For all others aspects of
the world, we use global constants, including graphical or visual
@ -447,7 +448,7 @@ Second, we must translate the actions in our domain---the arrows in the
teachpack can deal with. Once we have decided to use the passing of time
for one aspect, key presses for another, and mouse movements for a third,
we must develop functions that map the current state of the
world---represented as data from @tech{D}---into the next state of the
world---represented as data from @tech{WorldState}---into the next state of the
world. Put differently, we have just created a wish list with three
handler functions that have the following general contract and purpose
statements:
@ -455,16 +456,16 @@ Second, we must translate the actions in our domain---the arrows in the
@(begin
#reader scribble/comment-reader
(schemeblock
;; tick : @tech{D} -> @tech{D}
;; tick : WorldState -> WorldState
;; deal with the passing of time
(define (tick w) ...)
;; click : @tech{D} @emph{Number} @emph{Number} @tech{MouseEvent} -> @tech{D}
;; click : WorldState @emph{Number} @emph{Number} @tech{MouseEvent} -> WorldState
;; deal with a mouse click at @emph{(x,y)} of kind @emph{me}
;; in the current world @emph{w}
(define (click w x y me) ...)
;; control : @tech{D} @tech{KeyEvent} -> @tech{D}
;; control : WorldState @tech{KeyEvent} -> WorldState
;; deal with a key event (symbol, char) @emph{ke}
;; in the current world @emph{w}
(define (control w ke) ...)
@ -487,15 +488,14 @@ Our first and immediate goal is to represent the world as data. In this
the door is whether it is locked, unlocked but closed, or open. We use
three symbols to represent the three states:
@deftech{SD} : state of door
@(begin
#reader scribble/comment-reader
(schemeblock
;; The state of the door (SD) is one of:
;; WorldState is one of:
;; -- @scheme['locked]
;; -- @scheme['closed]
;; -- @scheme['open]
;; interpretation: state of door
))
Symbols are particularly well-suited here because they directly express
@ -535,14 +535,14 @@ a visible scene.}
]
Let's start with @emph{automatic-closer}. Substituting @tech{SD} for
@tech{D} and @emph{automatic-closer} for @emph{tick}, we get its contract,
Let's start with @emph{automatic-closer}. Since @emph{automatic-closer}
acts as the @scheme[on-tick] handler, we get its contract,
and it is easy to refine the purpose statement, too:
@(begin
#reader scribble/comment-reader
(schemeblock
;; automatic-closer : @tech{SD} -> @tech{SD}
;; automatic-closer : WorldState -> WorldState
;; closes an open door over the period of one tick
(define (automatic-closer state-of-door) ...)
))
@ -560,7 +560,7 @@ and it is easy to refine the purpose statement, too:
@(begin
#reader scribble/comment-reader
(schemeblock
;; automatic-closer : @tech{SD} -> @tech{SD}
;; automatic-closer : WorldState -> WorldState
;; closes an open door over the period of one tick
(check-expect (automatic-closer 'locked) 'locked)
@ -604,7 +604,7 @@ For the remaining three arrows of the diagram, we design a function that
@(begin
#reader scribble/comment-reader
(schemeblock
;; door-actions : @tech{SD} @tech{KeyEvent} -> @tech{SD}
;; door-actions : WorldState @tech{KeyEvent} -> WorldState
;; key events simulate actions on the door
(define (door-actions s k) ...)
))
@ -644,7 +644,7 @@ purpose:
@(begin
#reader scribble/comment-reader
(schemeblock
;; render : @tech{SD} -> @tech{scene}
;; render : WorldState -> @tech{scene}
;; translate the current state of the door into a large text
(define (render s)
(text (symbol->string s) 40 'red))
@ -719,9 +719,9 @@ Note the last clause includes @scheme[empty] of course.
Each world-producing callback in a world program---those for handling clock
tick events, keyboard events, and mouse events---may produce a
@tech{Package} in addition to just a @tech{World}.
@tech{Package} in addition to just a @tech{WorldState}.
@deftech{Package} represents a pair consisting of a @tech{World} (state)
@deftech{Package} represents a pair consisting of a @tech{WorldState}
and a message from a @tech{world} program to the @tech{server}. Because
programs only send messages via @tech{Package}, the teachpack does not
provide the selectors for the structure, only the constructor and a
@ -731,38 +731,38 @@ Each world-producing callback in a world program---those for handling clock
determine whether @scheme[x] is a @tech{Package}.}
@defproc[(make-package [w any/c][m sexp?]) package?]{
create a @tech{Package} from a @tech{World} and an @tech{S-expression}.}
create a @tech{Package} from a @tech{WorldState} and an @tech{S-expression}.}
As mentioned, all event handlers may return @tech{World}s or @tech{Package}s;
As mentioned, all event handlers may return @tech{WorldState}s or @tech{Package}s;
here are the revised specifications:
@defform/none[(on-tick tick-expr)
#:contracts
([tick-expr (-> (unsyntax @tech{World}) (or/c (unsyntax @tech{World}) package?))])]{
([tick-expr (-> (unsyntax @tech{WorldState}) (or/c (unsyntax @tech{WorldState}) package?))])]{
}
@defform/none[(on-tick tick-expr rate-expr)
#:contracts
([tick-expr (-> (unsyntax @tech{World}) (or/c (unsyntax @tech{World}) package?))]
([tick-expr (-> (unsyntax @tech{WorldState}) (or/c (unsyntax @tech{WorldState}) package?))]
[rate-expr natural-number/c])]{
}
@defform/none[(on-key change-expr)
#:contracts
([change-expr (-> (unsyntax @tech{World}) key-event? (or/c (unsyntax @tech{World}) package?))])]{
([change-expr (-> (unsyntax @tech{WorldState}) key-event? (or/c (unsyntax @tech{WorldState}) package?))])]{
}
@defform/none[(on-mouse clack-expr)
#:contracts
([clack-expr
(-> (unsyntax @tech{World}) natural-number/c natural-number/c (unsyntax @tech{MouseEvent})
(or/c (unsyntax @tech{World}) package?))])]{
(-> (unsyntax @tech{WorldState}) natural-number/c natural-number/c (unsyntax @tech{MouseEvent})
(or/c (unsyntax @tech{WorldState}) package?))])]{
}
If one of these event handlers produces a @tech{Package}, the content of the world
field becomes the next world and the message field specifies what the
world sends to the universe. This distinction also explains why the data
definition for @tech{World} may not include a @tech{Package}.
definition for @tech{WorldState} may not include a @tech{Package}.
@subsection{Connecting with the Universe}
@ -823,14 +823,14 @@ The @scheme[on-receive] clause of a @scheme[big-bang] specifies the event handle
@defform[(on-receive receive-expr)
#:contracts
([receive-expr (-> (unsyntax @tech{World}) sexp? (or/c (unsyntax @tech{World}) package?))])]{
([receive-expr (-> (unsyntax @tech{WorldState}) sexp? (or/c (unsyntax @tech{WorldState}) package?))])]{
tell DrScheme to call @scheme[receive-expr] for every message receipt, on the current
@tech{World} and the received message. The result of the call becomes the current
@tech{World}.
@tech{WorldState} and the received message. The result of the call becomes the current
@tech{WorldState}.
Because @scheme[receive-expr] is (or evaluates to) a world-transforming
function, it too can produce a @tech{Package} instead of just a
@tech{World}. If the result is a @tech{Package}, its message content is
@tech{WorldState}. If the result is a @tech{Package}, its message content is
sent to the @tech{server}.}
The diagram below summarizes the extensions of this section in graphical form.
@ -842,9 +842,9 @@ A registered world program may send a message to the universe server
message is transmitted to the server, which may forward it to some
other world program as given or in some massaged form. The arrival of a
message is just another event that a world program must deal with. Like
all other event handlers @emph{receive} accepts a @tech{World} and some
all other event handlers @emph{receive} accepts a @tech{WorldState} and some
auxiliary arguments (a message in this case) and produces a
@tech{World} or a @tech{Package}.
@tech{WorldState} or a @tech{Package}.
When messages are sent from any of the worlds to the universe or vice versa,
there is no need for the sender and receiver to synchronize. Indeed, a sender
@ -961,7 +961,7 @@ A @tech{server} keeps track of information about the @tech{universe} that
represented depends on the situation and the programmer, just as with
@tech{world} programs.
@deftech{Universe} @scheme[any/c] represent the server's state For running
@deftech{UniverseState} @scheme[any/c] represent the server's state For running
@tech{universe}s, the teachpack demands that you come up with a data
definition for (your state of the) @tech{server}. Any piece of data can
represent the state. We just assume that you introduce a data definition
@ -1010,16 +1010,16 @@ description. Two of them are mandatory:
@item{
@defform[(on-new new-expr)
#:contracts
([new-expr (-> (unsyntax @tech{Universe}) world?
(cons (unsyntax @tech{Universe}) [listof mail?]))])]{
([new-expr (-> (unsyntax @tech{UniverseState}) world?
(cons (unsyntax @tech{UniverseState}) [listof mail?]))])]{
tell DrScheme to call the function @scheme[new-expr] every time another world joins the
universe.}}
@item{
@defform[(on-msg msg-expr)
#:contracts
([msg-expr (-> (unsyntax @tech{Universe}) world? sexp?
(cons (unsyntax @tech{Universe}) [listof mail?]))])]{
([msg-expr (-> (unsyntax @tech{UniverseState}) world? sexp?
(cons (unsyntax @tech{UniverseState}) [listof mail?]))])]{
tell DrScheme to apply @scheme[msg-expr] to the current state of the universe, the world
that sent the message, and the message itself. The handler must produce a state of the
@ -1039,7 +1039,7 @@ optional handlers:
@item{
@defform/none[(on-tick tick-expr)
#:contracts
([tick-expr (-> (unsyntax @tech{Universe}) bundle?)])]{
([tick-expr (-> (unsyntax @tech{UniverseState}) bundle?)])]{
tell DrScheme to apply @scheme[tick-expr] to the current state of the
universe. The handler is expected to produce a bundle of the new state of
the universe and a list of mails.
@ -1047,7 +1047,7 @@ optional handlers:
@defform/none[(on-tick tick-expr rate-expr)
#:contracts
([tick-expr (-> (unsyntax @tech{Universe}) bundle?)]
([tick-expr (-> (unsyntax @tech{UniverseState}) bundle?)]
[rate-expr natural-number/c])]{
tell DrScheme to apply @scheme[tick-expr] as above but use the specified
clock tick rate instead of the default.
@ -1057,7 +1057,7 @@ optional handlers:
@item{
@defform[(on-disconnect dis-expr)
#:contracts
([dis-expr (-> (unsyntax @tech{Universe}) world? bundle?)])]{
([dis-expr (-> (unsyntax @tech{UniverseState}) world? bundle?)])]{
tell DrScheme to invoke @scheme[dis-expr] every time a participating
@tech{world} drops its connection to the server. The first argument is the
current state of the universe; the second one is the world that got
@ -1068,7 +1068,7 @@ optional handlers:
@item{
@defform[(to-string render-expr)
#:contracts
([render-expr (-> (unsyntax @tech{Universe}) string?)])]{
([render-expr (-> (unsyntax @tech{UniverseState}) string?)])]{
tell DrScheme to render the state of the universe after each event and to
display this string in the universe console.
}
@ -1134,7 +1134,7 @@ In summary, the first step of a protocol design is to introduce:
@itemize[
@item{a data definition for the information about the universe that the
server tracks, call it @tech{Universe};}
server tracks, call it @tech{UniverseState};}
@item{a data definition for the world(s) about their current relationship
to the universe;}
@ -1172,13 +1172,13 @@ translates into the design of two functions with the following headers,
@(begin
#reader scribble/comment-reader
(schemeblock
;; @tech{Universe} World -> (make-bundle @tech{Universe} [Listof mail?])
;; create new @tech{Universe} when world w is joining the universe,
;; @tech{UniverseState} world? -> (make-bundle @tech{UniverseState} [Listof mail?])
;; create new @tech{UniverseState} when world w is joining the universe,
;; which is in state s; also send mails as needed
(define (add-world s w) ...)
;; @tech{Universe} World MsgW2U -> (make-bundle @tech{Universe} [Listof mail?])
;; create new @tech{Universe} when world w is sending message m
;; @tech{UniverseState} world? MsgW2U -> (make-bundle @tech{UniverseState} [Listof mail?])
;; create new @tech{UniverseState} when world w is sending message m
;; to universe in state s; also send mails as needed
(define (process s p m) ...)
))
@ -1205,7 +1205,7 @@ As for the server's state, it must obviously keep track of all @tech{world}s tha
no @tech{world}s and, at that point, the server has nothing to track.
While there are many different useful ways of representing such a @tech{universe},
we choose to introduce @tech{Universe} as a list of @tech{world}s, and we
we choose to introduce @tech{UniverseState} as a list of @tech{world}s, and we
interpret non-empty lists as those where the first @tech{world} is active and the
remainder are the passive @tech{world}s. As for the two possible events,
@itemize[
@ -1249,7 +1249,7 @@ The preceding subsection dictates that our server program starts like this:
[schemeblock
;; teachpack: universe.ss
;; Universe is [Listof world?]
;; UniverseState is [Listof world?]
;; StopMessage is 'done.
;; GoMessage is 'it-is-your-turn.
])
@ -1264,13 +1264,14 @@ The preceding subsection dictates that our server program starts like this:
@(begin
#reader scribble/comment-reader
[schemeblock
;; Result is (make-bundle Universe (list (make-mail world? GoMessage)))
;; Result is
;; (make-bundle UniverseState (list (make-mail world? GoMessage)))
;; Universe world? -> Result
;; UniverseState world? -> Result
;; add world w to the universe, when server is in state u
(define (add-world u w) ...)
;; Universe world? StopMessage -> Result
;; UniverseState world? StopMessage -> Result
;; world w sent message m when server is in state u
(define (switch u w m) ...)
])
@ -1279,7 +1280,7 @@ Although we could have re-used the generic contracts from this
documentation, we also know from our protocol that our server sends a
message to exactly one world. For this reason, both functions return the
same kind of result: a bundle that contains the new state of the server
(@tech{Universe}) and a list that contains a single mail. These contracts
(@tech{UniverseState}) and a list that contains a single mail. These contracts
are just refinements of the generic ones. (A type-oriented programmer would
say that the contracts here are subtypes of the generic ones.)
@ -1311,7 +1312,7 @@ protocol.
The protocol tells us that @emph{add-world} just adds the given
@emph{world} structure---recall that this a data representation of the
actual @tech{world} program---to the @tech{Universe} and then sends a
actual @tech{world} program---to the @tech{UniverseState} and then sends a
message to the first world on this list to get things going:
@(begin
@ -1338,11 +1339,12 @@ Similarly, the protocol says that when @emph{switch} is invoked because a
[schemeblock
(define (switch univ wrld m)
(local ((define univ* (append (rest univ) (list (first univ)))))
(make-bundle univ* (list (make-mail (first univ*) 'it-is-your-turn)))))
(make-bundle univ*
(list (make-mail (first univ*) 'it-is-your-turn)))))
])
As before, appending the first world to the end of the list guarantees
that there is at least this one world on the next @tech{Universe}
that there is at least this one world on the next @tech{UniverseState}
(state). It is therefore acceptable to create a mail for this world.
Exercise: The function definition simply assumes that @emph{wrld} is
@ -1371,31 +1373,35 @@ The final step is to design the ball @tech{world}. Recall that each world
(schemeblock
;; teachpack: universe.ss
;; World is one of
;; WorldState is one of:
;; -- Number %% representing the @emph{y} coordinate
;; -- @scheme['resting]
(define WORLD0 'resting)
;; A WorldResult is one of:
;; -- WorldState
;; -- (make-package WorldState StopMessage)
))
The definition says that initially a @tech{world} is passive.
The communication protocol and the refined data definition of @tech{World}
The communication protocol and the refined data definition of @tech{WorldState}
imply a number of contract and purpose statements:
@(begin
#reader scribble/comment-reader
(schemeblock
;; World GoMessage -> World or (make-package World StopMessage)
;; WorldState GoMessage -> WorldResult
;; make sure the ball is moving
(define (receive w n) ...)
;; World -> World or (make-package World StopMessage)
;; WorldState -> WorldResult
;; move this ball upwards for each clock tick
;; or stay @scheme['resting]
(define (move w) ...)
;; World -> Scene
;; WorldState -> Scene
;; render the world as a scene
(define (render w) ...)
))
@ -1403,7 +1409,7 @@ The communication protocol and the refined data definition of @tech{World}
Let's design one function at a time, starting with @emph{receive}. Since
the protocol doesn't spell out what @emph{receive} is to compute, let's
create a good set of functional examples, exploiting the structure of the
data organization of @tech{World}:
data organization of @tech{WorldState}:
@(begin
#reader scribble/comment-reader
@ -1458,7 +1464,7 @@ the scene every time @scheme['it-is-your-turn] is received. Design this function
@(begin
#reader scribble/comment-reader
(schemeblock
; World -> World or @scheme[(make-package 'resting 'done)]
; WorldState -> WorldState or @scheme[(make-package 'resting 'done)]
; move the ball if it is flying
(check-expect (move 'resting) 'resting)
@ -1498,7 +1504,7 @@ Finally, here is the third function, which renders the state as a scene:
@(begin
#reader scribble/comment-reader
(schemeblock
; World -> Scene
; WorldState -> Scene
; render the state of the world as a scene
(check-expect (render HEIGHT) (place-image BALL 50 HEIGHT MT))
@ -1520,7 +1526,7 @@ Finally, here is the third function, which renders the state as a scene:
@(begin
#reader scribble/comment-reader
(schemeblock
; String -> (World -> Scene)
; String -> (WorldState -> Scene)
; render the state of the world as a scene
(check-expect
@ -1545,7 +1551,7 @@ Finally, here is the third function, which renders the state as a scene:
#reader scribble/comment-reader
(schemeblock
; String -> World
; String -> WorldState
; create and hook up a world with the @scheme[LOCALHOST] server
(define (create-world name)
(big-bang WORLD0