{"id":18014647,"url":"https://github.com/dhil/stratagem","last_synced_at":"2026-01-18T08:32:38.768Z","repository":{"id":76216578,"uuid":"377499206","full_name":"dhil/stratagem","owner":"dhil","description":"STRATAGEM is a research prototype system illustrating some theoretical ideas in game semantics.","archived":false,"fork":false,"pushed_at":"2023-04-05T11:04:37.000Z","size":74,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-04T15:13:42.701Z","etag":null,"topics":["continuations","game-semantics","sml"],"latest_commit_sha":null,"homepage":"https://homepages.inf.ed.ac.uk/jrl/Stratagem/index.html","language":"Standard ML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dhil.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-16T13:05:50.000Z","updated_at":"2022-05-09T23:24:32.000Z","dependencies_parsed_at":null,"dependency_job_id":"d83f10d4-e7cc-477d-a473-257551ddba63","html_url":"https://github.com/dhil/stratagem","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dhil/stratagem","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhil%2Fstratagem","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhil%2Fstratagem/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhil%2Fstratagem/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhil%2Fstratagem/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dhil","download_url":"https://codeload.github.com/dhil/stratagem/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhil%2Fstratagem/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28534148,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["continuations","game-semantics","sml"],"created_at":"2024-10-30T04:10:32.843Z","updated_at":"2026-01-18T08:32:38.751Z","avatar_url":"https://github.com/dhil.png","language":"Standard ML","funding_links":[],"categories":[],"sub_categories":[],"readme":"```sml\n(*********************************************************************)\n(*                                                                   *)\n(*                      STRATAGEM Version 1.1                        *)\n(*              User documentation and demo examples                 *)\n(*                                                                   *)\n(*     (C) John Longley, University of Edinburgh, December 2001      *)\n(*                      Version 1.1: June 2006                       *)\n(*                                                                   *)\n(*********************************************************************)\n(*                                                                   *)\n(* Walkthrough introduction to the STRATAGEM system, featuring some  *)\n(* simple ML programs as examples. Tested on NJ-SML v. 110.0.7.      *)\n(*                                                                   *)\n(*********************************************************************)\n```\nSTRATAGEM is a research prototype system illustrating some recent\ntheoretical ideas in game semantics. Given an already compiled ML\nfunction or operation supplied by the user, STRATAGEM extracts the\nunderlying \"computation strategy\" and allows it to be explored and\nmanipulated in various ways. Possible applications include execution\ntracing, interactive execution, and program optimization.\n\nThe system makes essential use of explicit continuations, and only\nruns on the New Jersey SML compiler. (I have tested it on version\n110.0.7 - please let me know if you have problems running it on other\nversions.)\n\nThe STRATAGEM system may be of interest to workers in the following areas:\n\n   * Game semantics. The system provides a \"workbench\" for one version\n     of game semantics, offers a convenient way of exploring the game\n     strategy for ML programs, and may be useful as a teaching tool.\n   * Program logics and verification. STRATAGEM offers a relatively\n     programmer-friendly way of understanding some ideas from semantics\n     that are likely to have applications to program logics in the near\n     future.\n   * Continuations. The system may be of interest as a non-trivial\n     example of a program making essential use of continuations.\n     It also seems to offer a helpful way of understanding much of what\n     continuations do, without the need for explicit continuation types.\n   * Execution tracing and debugging tools. STRATAGEM provides a kind\n     of execution tracing facility for ML programs, with some unusual\n     capabilities (e.g. interactive exploration of alternative execution\n     paths) which may be useful for debugging ML programs. Another\n     unusual feature is that the execution tracer runs within the same\n     ML session as the user's program - perhaps there is some interesting\n     way of exploiting this fact?\n   * Program optimization. STRATAGEM supplies a \"generic optimizer\" for\n     computation strategies. In some cases, this can lead to a genuine\n     speed-up for the user's already compiled program.\n   * Lazy and higher order programming. For programs involving lazy data\n     structures and higher order functions, it can often be difficult to\n     understand their run-time behaviour operationally - \"what happens\n     when\". The execution tracing facilities of STRATAGEM may be useful\n     for understanding such programs better.\n   * Teaching ML. For newcomers to ML, STRATAGEM might be useful as a\n     teaching tool - for example, for viewing the recursion unfoldings of\n     a recursively defined function.\n\nThe current version (1.1) is intended as a concept demonstrator rather than\na serious debugging tool - the full range of ML datatypes is not yet\nsupported - but it suffices to illustrate all the basic ideas and allows\nthe user to perform some complex and interesting experiments.\n\nGetting started\n---------------\n\nTo run STRATAGEM, you need the New Jersey implementation of ML, and\nthe source file \"stratagem.sml\" which is available online at\n\n       http://homepages.inf.ed.ac.uk/jrl/Stratagem/stratagem-1.1.sml\n\nStart up a New Jersey SML session from a directory containing the file\n\"stratagem.sml\". [On the Informatics machines in Edinburgh, this is\ndone by typing \"sml\" to the Linux prompt.] Then type:\n\n       use \"stratagem-1.1.sml\" ;\n\nThis builds the system.\n\nYou will see that a structure called `Code_Gen` has been opened,\ncontaining (among other things) a recursive datatype `Type`, for\nrepresenting the syntax trees of (a class of) ML types.  Values of\ntype \"Type\" can be expressed in a form which mimics the ML syntax of\ntypes. For example,\n\n```sml\n  (Int Times Bool Arrow Unit) Arrow Int `List ;\n```\n\nrepresents the ML type `(int * bool -\u003e int) -\u003e int list`.\n\n[NOTE: The precedence and binding rules are just as in ML syntax,\n except that only binary products are supported as primitive, so that\n\n```sml\n  Int Times Int Times Int\n```\ncorresponds to `(int * int) * int`, rather than to `int * int * int`.\nNote also that has been declared as an infix, allowing us to write\nInt List instead of List Int.]\n\nUsually one wants to use STRATAGEM in conjunction with an ML program\nof some type t determined by the user. To do this, one first needs\nto create a bunch of operations specific to the type t.\nAs an example, suppose we wish to use STRATAGEM with a user program\nof type `(int -\u003e int) -\u003e int`. We first perform the following:\n\n```sml\n  generate_tools \"foo\" ((Int Arrow Int) Arrow Int) ;\n```\n\nThis automatically generates a short source file \"foo.sml\" (overwriting\nany existing file of that name), and loads it in. (If you want to change\nthe directory into which this file is written, use \"chDir\" first.)\nThe file \"foo.sml\" declares a structure \"foo\" containing operations\nspecific to the required type. To make use of this, first type:\n\n```sml\n  open foo ;\n```\nNote that not all ML datatypes are yet supported in this version - not\neven all those representable using `Type`. (In fact, the only types\nsupported at present are equality types and types built up from them\nusing `-\u003e`.) The system will let you know if you try to run\n`generate_tools` on a type not yet supported. [There is no obstacle in\nprinciple to supporting all the (functional) datatypes of ML - I just\nhaven't got round to it yet!]\n\nExecution tracing\n-----------------\n\nYou may now write any ML program you like of type `(int -\u003e int) -\u003e int`.\nAs a simple example, consider\n\n```sml\n  fun G f = if f 3 \u003c 6 then f 1 else f (f 2 + 1) ;\n```\n\nWhen `G` is applied to a function `f : int -\u003e int`, the resulting computation\ncan be viewed as a kind of interaction or dialogue between G and f.\nThe operation foo.trace allows us to see this dialogue unfolding, e.g.\n\n```sml\n  trace G (fn x =\u003e x+2) ;\n\n  trace G (fn x =\u003e x*x) ;\n```\n\nWhat happens here is that \"trace `G`\" evaluates to an operation whose\nbehaviour is identical to that of `G`, except that it outputs a lot of\ntrace information showing the interactions between (the strategy for)\n`G` and the rest of the program context.\n\nThe form in which the dialogue is displayed may call for some\nexplanation.  First, the type in question (here, the type of `G`) is\ndisplayed, and under each occurrence of \"`-\u003e`\" we have a (numbered)\ncolumn in which moves can occur. The idea is that for a function of\nany type `t -\u003e t'`, there are basically two kinds of interaction that\nmay occur. Either the function may be interrogated for its value at a\ncertain argument, or the function may return such a value: we think of\nthese moves as questions (`Q`) of type `t`, or as answers (`A`) of type\n`t'`. Depending on the types, both questions or answers can be printable\nvalues of ground types (such as int), or unprintable values of\nfunction types displayed simply as `-`. In the latter case, t' will\nitself have the form `u -\u003e u'`, and so more information about the value\nrepresented by `-` can be given by questions and answers in some other\ncolumn.\n\n[ NOTE FOR SPECIALISTS: The above conventions are slightly different\n  from the usual ones in most papers on game semantics, in which (a)\n  the columns are headed by occurrences of ground types rather than\n  occurrences of `-\u003e`; and (b) questions are not allowed to carry\n  values; there is just a single question \"?\" in each column. These\n  differences reflect the way we have chosen to model ML's\n  CALL-BY-VALUE evaluation mechanism: one cannot ask a question to a\n  function without having a value to offer (operationally, either a\n  ground-type value or a closure, i.e. a weak head normal form).  Our\n  approach to modelling call-by-value seems close to that of Abramsky\n  and McCusker [see Proc. 11th CSL, 1998]. An alternative approach to\n  game semantics for call-by-value languages, superficially less close\n  to ours, has been proposed by Honda and Yoshida (see Proc. 25th\n  ICALP, 1997). ]\n\nNote that moves alternate between two participants: a player `P`,\nrepresenting `G` itself, and an opponent `O`, representing the environment\nto `G`. In this case, `O` represents the function supplied as an argument\nto `G`, as well as the top-level request for the value of `G` when applied\nto this function.\n\nFinally, the rightmost column displays what are known as JUSTIFICATION\nPOINTERS. For every move except the first, the corresponding pointer\nindicates the earlier move to which the present move \"refers\". In the\ncase of answer moves, the pointer indicates the question (in the same\ncolumn) which the move answers. In the case of question moves, the\npointer indicates the move which introduces the function (represented\nby `-`) to which the question is being posed. In the case of the\ninitial question, there is no justification pointer - the function to\nwhich the question is posed is the function we are tracing (`G` in the\nabove example).\n\nRemarks on execution tracing\n----------------------------\n\n1. For the above examples (and those to follow), you should satisfy yourself\n   that the trace displayed corresponds to the evaluation behaviour you\n   would intuitively expect from your ML program. We hope that in this way\n   readers will be able to absorb many of the essential ideas of game\n   semantics fairly easily.\n\n2. We hope that our execution tracing facility might (eventually) be\n   useful for debugging programs, understanding their operational\n   behaviour, and detecting sources of inefficiency.\n\n   With respect to the last of these, however, there is a subtle\n   limitation that we should point out. If an ML compiler is clever,\n   it might (for all we know) be performing optimizations that take\n   advantage of the fact that the user's program is free from side effects,\n   e.g. it may eliminate some redundant function calls. However, if we\n   now demand a trace for this program, the mere fact of requiring the\n   system to output the trace information may be enough to block some of\n   these optimizations! Thus, the trace we see when using \"trace G\" might\n   not be a reliable indication of the evaluation behaviour of the pure\n   version of `G` after compiler optimization. (In other words, the attempt\n   to observe the behaviour of a program may itself affect the thing we\n   are trying to observe!)\n\n   [I do not know whether the above possibility can actually arise in the\n   case of the New Jersey compiler.]\n\nInteractive mode\n----------------\n\nThe above shows how we can do execution tracing in \"batch mode\": we\ntrace the entire evaluation of an ML expression. However, one can also\nstep through the execution of a program in interactive mode: here the\nmachine plays the function we are tracing (such as G), and the user\nplays its environment. In terms of the game, the machine is Player and\nthe user is Opponent. To see this for the above program, type\n\n```sml\n  start_game G ;\n```\n\nTo play the first move, we have to ask the initial question, demanding\nthe final result of G. We do this by typing\n\n```sml\n  Q1 () ;\n```\n\nHere the `Q` means we are playing a question; the \"1\" means we are\nplaying it in column 1; the `()` is there as a dummy to indicate that\nthe question carries no printable value (it appears in the trace as\n`Q-`).  The system responds by pretty-printing the trace of your move\n(labelled as `O1`) together with the machine's response (labelled as\n`P1`).  In the case of the above `G`, the machine is posing the\nquestion `Q 3` to the function `-` which we have notionally supplied.\n\nTo play the second move, we now need to pretend we are some argument\n`f : int -\u003e int`, whose behaviour we may make up as we go along.  For\ninstance, let us pretend we are the function `(fn x =\u003e x+2)`.  We\ntherefore want to supply the answer `5` to the machine's question.  To\ndo this we type\n\n```sml\n  A0 5 ;\n```\n\nwhich means: play an answer in column 0, carrying the value 5.  Once\nagain, the system displays our move together with Player's response.\n\nSuppose now that we now change our mind about our last move, or decide\nwe would like to explore what would have happened if we had played\ndifferently. We can withdraw our last move by typing\n\n```sml\n  backup 1 ;\n```\n\nwhich means \"backup by one step\". The last-but-one pair of moves,\nnamely `O1` and `P1`, will now be re-displayed, reminding us of the game\nstate we are now in. Let us now pretend we are the function `(fn x =\u003e\nx*x)`. We can now play the alternative move\n\n```sml\n  A0 9 ;\n```\n\nEXERCISE: continue the game as if you were the playing the function\n`(fn x =\u003e x*x)`. It will take you two more moves to conclude the game.\nYou will see that the accumulated trace is the same as that obtained\nin batch mode by typing\n\n```sml\n  trace G (fn x =\u003e x*x) ;\n```\n\nExplicit justifiers\n-------------------\n\nYou will notice that in the above example it was not necessary to\nsupply explicit justification pointers - the system was able to\ncompute them automatically. Most of the time this works fine, since\nusually there is only one possible justifier for the given move in any\ncase.  In more complicated cases, however, there is a choice of\npossible justifiers, so it is sometimes necessary to supply the\npointers explicitly.  (An example will be given below.) For this\npurpose, the system provides primed variants `A0'`,`Q1'` of the\nmove-playing operations `A0`,`Q1`, which take as an extra argument a\njustification pointer - either (init) as a null pointer for the\ninitial move, or (by `n`) as a pointer to the move `Pn`.  (Note that\nOpponent moves, except the initial move, are always justified by\nPlayer moves.)\n\nThus, the above game could be replayed as follows if we insisted on\nsupplying the pointers ourselves:\n\n```sml\n  start_game G ;\n  Q1' () (init) ;\n  A0' 9  (by 1) ;\n```\n\netc.\n\nIf a \"bad\" pointer is supplied, the system will usually complain with\nan error report.  [NOTE: at present the error trapping on pointers is\nnot quite complete.  Indeed, the system may sometimes appear to accept\nan invalid pointer but will \"correct\" it to a valid one. We hope these\nminor problems will be fixed in the next version!]\n\nRemarks on interactive mode\n---------------------------\n\n1. In the current version, only one interactive game play may\n   be in progress at a time. An interactive game play is initiated by\n   `start_game`; thereafter, the system maintains a stack of game states to\n   enable \"backup\" to work. This stack is cleared the next time `start_game`\n   is called.\n\n2. In the above example, the operations `A0` and `Q1` (and their primed variants)\n   are declared in the structure `foo`. When the file `foo.sml` is generated,\n   the system calculates the columns, types and `Q`/`A` attributes of all possible\n   Opponent moves. If the user attempts to play a move which for some reason\n   is illegal in the context, the system will (usually) respond with an\n   error report.\n\n3. If in an interactive play the system asks us the same question twice,\n   there is no reason why we have to supply the same answer both times.\n   In other words, we might be pretending we are some argument with mutable\n   internal state, whose behaviour can depend on this state. See also the\n   remarks on program optimization and the example `H` below.\n\n4. For a similar reason, if the program we are tracing makes use of state,\n   its behaviour may depend on the current contents of the state.\n   We therefore cannot expect the \"backup\" facility always to work properly\n   for such programs, since the state may have changed since we were last\n   in a certain position in the game!\n\n5. It is worth reflecting on what the system is doing during interactive\n   mode. In the above example, at each stage the system performs a bit of\n   the computation of `G` applied to an argument. Whenever `G` requires\n   information about this argument, the state of the whole computation is\n   \"frozen\", and the system jumps out of it and returns to the top-level\n   ML prompt. The user can then supply the required piece of information,\n   at which point the frozen computation is resumed using this new\n   information. It should be clear that explicit continuations are absolutely\n   essential to achieve this effect of freezing computations and later\n   resuming them.\n\nFormat controls\n---------------\n\nThe structure `Display.Format` contains a few parameters which may be\nadjusted to vary the format of the displayed game trace (in both batch\nand interactive mode). For instance, to suppress the line of\nwhitespace between consecutive pairs of moves, type\n\n```sml\n  open Display.Format ;\n  blank_lines := false ;\n```\n\nThe parameter `Display.Format.column_width` can be used to vary the\nspacing between columns. This might be useful if the values involved\nwere not just integers but e.g. bool lists, in which case increasing\nthe column width would stop the columns from running into each other\nand improve legibility. (If you require wide columns *and* lots of\nthem, stretch your window horizontally!)\n\nAt the beginning of a (batch or interactive) trace, the system\ndisplays the type in question, together with the associated column\nnumbers, according to the current format settings. To re-display this\ntype at any stage, one may call the function \"foo.show_type\" :\n\n```sml\n  show_type () ;\n```\n\n(This is useful, for instance, during a long interactive game play\n when the type and column numbers have scrolled off the top of the\n screen.)\n\nProgram optimization\n--------------------\n\nAnother feature of STRATAGEM is that it allows the user's programs to\nbe automatically \"optimized\" (in a certain sense) after they have been\ncompiled. (This application was suggested to us by Mike Fourman.)  For\nexample, consider the program\n\n```sml\n  fun H f = f 3 + f (f 3) ;\n```\n\nWhen `H` is applied to some function `f : int -\u003e int`, the call `f 3` will\nnormally be performed twice. We can see this by typing e.g.\n\n```sml\n  trace H (fn x =\u003e x*2) ;\n```\n\nSuppose, however, we define\n\n```sml\n  val H' = optimize H ;\n```\n\nThen `H'` has the same functional behaviour as `H`, but the underlying\nstrategy has been \"optimized\" to eliminate redundancies. To see this,\ndo\n\n```sml\n  trace H' (fn x =\u003e x*2) ;\n```\n\nNote that the second call to `f 3` has been omitted, and the\ncomputation proceeds as if it yielded the same value as the first call\nto `f 3`.  The effect is even more marked if we perform\n\n```sml\n  trace H  (fn x =\u003e x*2-3) ;\n  trace H' (fn x =\u003e x*2-3) ;\n```\n\nHere, three calls to `f 3` are collapsed to a single call. This shows\nthat the optimization is in some sense happening dynamically - in this\nexample, it is reminiscent of memoization. Below we will see examples\ninvolving more complex types - in these cases one can perhaps think of\n\"optimize\" as performing some kind of very generic \"blanket\nmemoization\" at all possible levels.\n\nRemarks on optimization\n-----------------------\n\n1. Note that this kind of optimization will not in general work if there is\n   internal state around. If f is an \"object\" with some internal state that\n   may be updated when f is called, there is no guarantee that the value of\n   `f 3` for the second time of asking will be the same as for the first,\n   so `H f` and `H' f` may yield different results.\n\n   The general point is that if we assume certain constraints on the kind of\n   computation involved, this allows us to perform certain kinds of\n   optimization. It seems that there are other instances of this phenomenon\n   awaiting exploring - what we have done is only a first step in this\n   direction.\n\n2. The reader is warned that the above is only \"optimizing\" `H` in the limited\n   sense of minimizing calls to `f`. The process of computing `H'` from `H` is\n   quite complex and involves a significant overhead. It will not, in the\n   above example, result in a genuine speed-up, although of course it would\n   give a genuine improvement in performance in cases where calls to `f` were\n   sufficiently costly to compute.\n\n3. Our optimization is not exactly memoization in the usual sense, since\n   (in the above example) our H' does not actually involve any persistent\n   internal state. Thus, for instance, if we perform\n```sml\n        trace H' (fn x =\u003e x*2) ;\n```\n   twice, we still need to ask for `f 3` and `f 6` the second time.\n   See below for an example of execution tracing for a genuine memoization\n   operation.\n\nMore examples\n-------------\n\nWe now present some slightly more complex examples, which serve to\nillustrate a few additional points. Just work your way through these\nuntil you get bored ;\u003e)\n\n1. An example involving a slightly more complicated type.\n   Perform the following:\n```sml\n       val ty1 = Int Arrow Int ;\n       generate_tools \"bar\" (ty1 Arrow ty1) ;\n       open bar ;\n```\nThe following program illustrates Ulam's famous `3n+1` problem.  The\nfunction \"chase\" takes a function `f : int -\u003e int` and a starting\nvalue; it iterates `f` until the value `1` is reached, and returns the\nnumber of iterations required. The function \"ulam\" performs a single\nstep of the iteration involved in the problem: if the number is even,\nhalve it, otherwise treble it and add `1`.\n```sml\n        local\n\t    fun chase' k f n =\n\t        if n = 1 then k\n\t        else chase' (k+1) f (f n)\n        in\n\t    val chase = chase' 0\n        end ;\n\n       fun ulam n =\n       if n mod 2 = 0\n       then n div 2 else n*3+1 ;\n```\n\nA famous open problem in mathematics is whether `chase ulam n`\nterminates for all integers `n\u003e=1`.\n\nTo get an execution trace for this program, type e.g.\n\n```sml\n       trace chase ;\n       it ulam ;\n       it 5 ;\n```\nThis illustrates an important point about the behaviour of (curried)\nfunctions of several arguments in ML. In principle, a bit of\ncomputation happens each time an argument is supplied - it is not all\ndelayed until a result of ground type is required. (After all, it is\npossible in ML to write a function `F : (int-\u003eint)-\u003e(int-\u003eint)` such\nthat `F ulam` diverges!)\n\nFor a more spectacular example, try\n\n       trace chase ulam 27 ;\n\nEXERCISE: Play through the computation of `chase ulam 5` in\ninteractive mode.\n\n2. A simple example involving ground types other than `int`:\n   an instance of the map operation for lists.\n```sml\n        generate_tools \"map\"\n           ((Int Arrow Bool) Arrow Int `List Arrow Bool `List) ;\n        open map ;\n\n        Display.Format.column_width := 20 ;\n        trace List.map (fn x =\u003e x\u003c=5) [3,8,5] ;\n```\nEXERCISE: Play through this game in interactive mode.\n\n3. Execution tracing for the standard memoization operation.\n   Shows the different call behaviour the second time we ask for some value.\n   Uses the same type as example 1 above.\n```sml\n        val ty1 = Int Arrow Int ;\n        generate_tools \"bar\" (ty1 Arrow ty1) ;\n        open bar ;\n\n        (* Usual implementation of memoization for functions int -\u003e int *)\n        local\n\t    datatype 'a option = None | Some of 'a\n\t    fun lookup x [] = None\n\t     | lookup x ((y,z)::rest) =\n\t       if x=y then Some z else lookup x rest\n        in\n\t    fun memo f =\n\t       let val cache = ref [] in\n\t\t   fn x =\u003e\n\t\t   (case lookup x (!cache) of\n\t\t\tSome z =\u003e z\n\t\t      | None =\u003e\n\t\t\t    case f x of z =\u003e\n\t\t\t\t(cache := (x,z)::(!cache) ; z))\n\t       end\n        end ;\n\n        Display.Format.column_width := 10 ;\n        val sq = trace memo (fn x =\u003e x * x) ;\n        sq 5 ;\n        sq 5 ;\n```\n4. A more complex example: tracing the recursive call structure for the\n   (slow) Fibonacci function.\n```sml\n        val ty1 = Int Arrow Int ;\n        generate_tools \"zog\" ((ty1 Arrow ty1) Arrow ty1) ;\n        open zog ;\n```\nTo watch the unfoldings of a recursively defined function of type `int\n-\u003e int`, we cannot quite use the already compiled version of the\nfunction, since what we wish to observe are the interactions between\nthe fixed point operator and the \"body\" of the function definition.\nIn this case, we therefore need to edit our source code a little.  In\nthe case of the Fibonacci function, instead of\n```sml\n       fun f 0 = 0\n         | f 1 = 1\n         | f n = f(n-2) + f(n-1) ;\n```\nwe need to declare\n```sml\n       fun Y F x = F (Y F) x ;\n       fun F f 0 = 0\n\t | F f 1 = 1\n\t | F f n = f(n-2) + f(n-1) ;\n```\nClearly `Y F n` yields the nth Fibonacci number. Now try:\n```sml\n       trace Y F 2 ;\n```\nSatisfy yourself that the displayed trace is what it should be, and\nshows the recursive unwindings of F. If you like, play through the ten\nmoves of the game in interactive mode.\n\nThe length of the game for `Y F n` grows exponentially with `n`.  For\ninstance, try:\n```sml\n       trace Y F 10 ;\n```\nHowever, here we can use our optimizer to good effect. Try:\n```sml\n       val Y' = optimize Y ;\n       trace Y' F 10 ;\n```\nThis brings the length of the game down to something linear in `n`.  [Of\ncourse, the total run-time of `Y' F n` is still exponential in `n` - it\nis only the number of interactions between `Y'` and `F` that is linear!\nEven so, this example shows that our optimizer is achieving something;\nperhaps the main interest lies in the fact that such a \"generic\"\noptimizer as ours can achieve these results.]\n\n5. An example showing how the use of exceptions and references can give\n   rise to games with interesting features.\n```sml\n        generate_tools \"sr\" (((Unit Arrow Unit) Arrow Unit) Arrow Bool) ;\n        open sr ;\n```\nLet top, `bot : unit -\u003e unit` be the functions defined by\n```sml\n       fun top () = () and bot () = bot () ;\n```\nConsider the function `F` of the above type, specified by\n```sml\n    (*  F g = true   if g top = (), but g bot diverges;\n        F g = false  if g bot = ();\n        F g diverges otherwise.                         *)\n```\n[This is my favourite simple example of a SEQUENTIALLY REALIZABLE\n function. For more information, see the ML source file: \"When is a\n functional program not a functional program: a walkthrough\n introduction to the sequentially realizable functionals\" available\n from my home page.]\n\nThis function may be implemented in ML in various ways.  First, using\na local reference:\n```sml\n       fun F1 g =\n\t   let val r = ref false in\n\t       (g (fn () =\u003e r:=true) ; !r)\n\t   end ;\n\n       trace F1 (fn x =\u003e ()) ;\n       trace F1 (fn x =\u003e x()) ;\n```\nWe can see here that Player's final move is sensitive to what has gone\non between the question and answer in column 1. In game semantics\nterms, this is an example of *non-innocent* behaviour - something\ngenerally associated with the presence of state in computations.\n\nPlay through the above computations interactively if you like.  Rather\nsurprisingly perhaps, \"backup\" works nicely here and seems to restore\nthe previous contents of the local register r:\n```sml\n       start_game F1 ;\n       Q2 () ;\n       Q0 () ;\n       backup 1 ;\n       A1 () ;\n```\n(This is something I didn't know about the semantics of continuations.\n It certainly doesn't work like this if you use a global register!)\n\nNow another implementation of F using exceptions.\n```sml\n       fun F2 g =\n\t   let exception e in\n\t       (g (fn () =\u003e raise e) ; false)\n\t       handle e =\u003e (g (fn () =\u003e ()) ; true)\n           end ;\n```\nAlthough `F2` computes the same function as `F1`, the underlying computation\nstrategy is different, as can be seen by trying\n```sml\n       trace F2 (fn x =\u003e x()) ;\n```\nThis example is of interest because the questions `P1` and `O2` are left\n\"dangling\" and never answered. In fact, this is an example of a\n*non-well-bracketed* computation, since the final move `P4` does NOT\nmatch the most recent unanswered question. This is a characteristic\ngenerally associated with programs involving control features, such as\nexceptions and continuations.\n\n[For the record, there's also an implementation of `F` using\n continuations; its underlying strategy is the same as that of `F2`.]\n\n6. An example in which explicit justification pointers are necessary.\n   One needs to go up to a fourth-order ML function for this to happen\n   (though morally, the following example lives at call-by-name type 3).\n```sml\n        generate_tools \"four\"\n          ((((Unit Arrow Unit) Arrow Unit) Arrow Unit) Arrow Unit) ;\n        open four ;\n\n        fun Phi F = F (fn t =\u003e t()) ;\n\n        fun F1 g = g (fn () =\u003e g (fn () =\u003e ())) ;\n        fun F2 g = g (fn () =\u003e\n\t\t     let exception e in\n\t\t\t (g (fn ()=\u003eraise e)) handle e =\u003e ()\n\t\t     end) ;\n```\nNow try\n```sml\n        trace Phi F1 ;\n        trace Phi F2 ;\n```\nNote that the two plays are identical up to move `O4`, and this move\ndiffers only in its choice of justification pointer.  Suppose we try:\n```sml\n       start_game Phi ;\n```\nand play the game interactively until we reach this point.  We then\nhave a genuine choice whether to play `A0' ()` (by 3) or `A0' ()` (by\n2); the system will accept either of these and play the strategy for\n`Phi` accordingly.  Note that if we simply enter `A0 ()`, the system\nwill by default select the MOST RECENT of the possible justifying\nmoves.  This will usually be the one we want if we are playing a\nPCF-style strategy (i.e. one corresponding to a purely functional ML\nprogram).\n\nEven within the world of PCF-style computations, there are cases when\na genuine choice of pointer is necessary, but one needs to go to a\nfifth-order ML function to see this!\n\nRules of the game\n-----------------\n\nSo far we have not been very precise about the rules applying to games\nin our setting - e.g., what constitutes a \"legal move\" in an\ninteractive play - but have been relying mainly on ML programmer\nintuition.  For advanced experiments, however, one needs to understand\nthe rules more precisely. For the record, here are the rules for\nOpponent moves (i.e. the user's moves), in a rather terse form.\n\nOpponent moves may be of three kinds:\n   * The initial move. This must be a question, and must appear in the\n     column corresponding to the top-level occurrence of `-\u003e` in the\n     type in question. The justification pointer must be null, i.e. \"init\".\n   * Non-initial questions. These must appear in a column corresponding\n     to a non-top-level occurrence `x` of `-\u003e`, and must be justified by a\n     player question in the column corresponding to the occurrence of `-\u003e`\n     directly above `x` in the syntax tree of the type.\n   * Answers. These can appear in any column, and must be justified by a\n     player question in the same column.\n\nMoreover, the justifying move for a non-initial move must be VISIBLE\nat the current point in the game. That is, the justifying move for the\nmove O(n+1) (where n\u003e=1) must appear in the *O-view* of the sequence\n`O1`,`P1`,...,`On`,`Pn`. The O-view of a justified sequence of moves may be\ndefined inductively as follows:\n\n        O-view () = ()\n        O-view (O1,P1,...,Om)    = O-view (O1,...,P(m-1)) . Om\n        O-view (O1,P1,...,Om,Pm) = O-view (O1,...,P(k-1)) . Ok . Pm,\n                                   where Ok is the justifier of Pm.\n\nOur game protocol therefore coincides with the most liberal of the\nfour protocols studied in Abramsky and McCusker, \"Game semantics\"\n(Proc. 1995 Marktoberdorf Workshop); see also McCusker's thesis.\n\nHow it works (very brief note for specialists!)\n-----------------------------------------------\n\nThe theory underpinning our system is closely concerned with the\ncorresponding category in the Abramsky/McCusker framework. The key\nobservation is that in this category (or at least a closely related\none), there is a universal type, namely\n```sml\n       datatype forest = forest of nat -\u003e (nat * forest)\n```\nWe can think of this as the type of strategies for the \"generic game\",\nin which two participants take turns to play elements of nat.  The\ncore of our system is the module `Forest`, which implements the\noperations that make the type `forest` into a reflexive object.  Once\nthis is done, it is not hard to represent all other types t as\ncomputable retracts of `forest`; the embedding half of the retraction\nwill then be an operation which extracts the underlying computation\nstrategy of a given operation.\n\nThere's a lot more to say here - the full story will be told in detail\nin a forthcoming research paper!\n\nFinal tips\n----------\n\n1. Two common mistakes (in my experience!) which can lead to very bizarre\n   results are:\n   * Forgetting that you haven't opened an automatically generated structure\n     such as `bar`. This can mean that e.g. when playing an interactive\n     game, you are inadvertently using operations belonging to a different\n     structure, such as `foo`. The results can be most puzzling!\n   * Forgetting that you haven't actually initiated an interactive game play,\n     Once again, you may find yourself working on a state left over from\n     some previous game play.\n\n2. It is possible (though rare in practice) that if you stop the execution\n   of some operation using a keyboard interrupt (usually, because it appears\n   to be diverging), the system will be left in an inconsistent state,\n   which may manifest itself in bizarre outcomes to future experiments.\n   If you are worried that this may have happened, type\n```sml\n        reset () ;\n```\n   to restore the system to a consistent state.\n\n3. If you are unscrupulous, and don't have much use for the arithmetical\n   minus operation, you may perform the following:\n```sml\n        nonfix - ;\n        val - = () ;\n```\n   This means that in interactive mode you can play moves with non-printable\n   values by typing things like `Q1 - ;` instead of `Q1 () ;`.\n   This gives the system a somewhat more user-friendly feel.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdhil%2Fstratagem","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdhil%2Fstratagem","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdhil%2Fstratagem/lists"}