about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-10-05 18:40:51 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-10-05 18:40:51 -0700
commitd135851ef94bf15f258b779098404ef6adac92b6 (patch)
tree486b24923abc700b3ff2e41d6f9a1220c31e79d7
parent745e8bdd7ad66ebfb0a282f820c93d3a544b2afe (diff)
downloadmu-d135851ef94bf15f258b779098404ef6adac92b6.tar.gz
2247 - type-check products of non-primitive recipes
We still can't check ingredient types, and even this is still a run-time
check. We'll need to start tracking recipe signatures at some point.

I've had to introduce a hack called /skiptypecheck. Time to get generics
working.
-rw-r--r--036call_reply.cc33
-rw-r--r--061channel.mu24
-rw-r--r--065duplex_list.mu72
-rw-r--r--071print.mu2
-rw-r--r--081run_interactive.cc6
-rw-r--r--channel.mu12
-rw-r--r--edit/005-sandbox.mu4
-rw-r--r--edit/006-sandbox-edit.mu6
-rw-r--r--edit/011-editor-undo.mu18
-rw-r--r--sandbox/005-sandbox.mu4
-rw-r--r--sandbox/006-sandbox-edit.mu6
-rw-r--r--sandbox/011-editor-undo.mu18
12 files changed, 114 insertions, 91 deletions
diff --git a/036call_reply.cc b/036call_reply.cc
index ce231d19..14fb0c5c 100644
--- a/036call_reply.cc
+++ b/036call_reply.cc
@@ -30,12 +30,22 @@ case REPLY: {
   // just in case 'main' returns a value, drop it for now
   if (Current_routine->calls.empty()) goto stop_running_current_routine;
   const instruction& caller_instruction = current_instruction();
+  // check types with the caller
+  if (SIZE(caller_instruction.products) > SIZE(ingredients)) {
+    raise << "too few values replied from " << callee << '\n' << end();
+    break;
+  }
+  for (long long int i = 0; i < SIZE(caller_instruction.products); ++i) {
+    if (has_property(caller_instruction.products.at(i), "skiptypecheck")) continue;  // todo: drop this once we have generic containers
+    if (!types_match(caller_instruction.products.at(i), reply_inst.ingredients.at(i))) {
+      raise << maybe(callee) << "reply ingredient " << reply_inst.ingredients.at(i).original_string << " can't be saved in " << caller_instruction.products.at(i).original_string << '\n' << end();
+      goto finish_reply;
+    }
+  }
   // make reply products available to caller
   copy(ingredients.begin(), ingredients.end(), inserter(products, products.begin()));
   // check that any reply ingredients with /same-as-ingredient connect up
   // the corresponding ingredient and product in the caller.
-  if (SIZE(caller_instruction.products) > SIZE(ingredients))
-    raise << "too few values replied from " << callee << '\n' << end();
   for (long long int i = 0; i < SIZE(caller_instruction.products); ++i) {
     trace(Primitive_recipe_depth, "run") << "result " << i << " is " << to_string(ingredients.at(i)) << end();
     if (has_property(reply_inst.ingredients.at(i), "same-as-ingredient")) {
@@ -64,12 +74,25 @@ recipe main [
 recipe f [
   12:number <- next-ingredient
   13:number <- copy 35
-  reply 12:point/raw  # unsafe
+  reply 12:point/raw
 ]
 +run: result 0 is [2, 35]
 +mem: storing 2 in location 3
 +mem: storing 35 in location 4
 
+:(scenario reply_type_mismatch)
+% Hide_warnings = true;
+recipe main [
+  3:number <- f 2
+]
+recipe f [
+  12:number <- next-ingredient
+  13:number <- copy 35
+  14:point <- copy 12:point/raw
+  reply 14:point
+]
++warn: f: reply ingredient 14:point can't be saved in 3:number
+
 //: In mu we'd like to assume that any instruction doesn't modify its
 //: ingredients unless they're also products. The /same-as-ingredient inside
 //: the recipe's 'reply' will help catch accidental misuse of such
@@ -82,8 +105,8 @@ recipe main [
   2:number <- test1 1:number  # call with different ingredient and product
 ]
 recipe test1 [
-  10:address:number <- next-ingredient
-  reply 10:address:number/same-as-ingredient:0
+  10:number <- next-ingredient
+  reply 10:number/same-as-ingredient:0
 ]
 +warn: main: 'same-as-ingredient' product from call to test1 must be 1:number rather than 2:number
 
diff --git a/061channel.mu b/061channel.mu
index 981d6c0c..c8ca6cc9 100644
--- a/061channel.mu
+++ b/061channel.mu
@@ -12,7 +12,7 @@ scenario channel [
   run [
     1:address:channel <- new-channel 3/capacity
     1:address:channel <- write 1:address:channel, 34
-    2:number, 1:address:channel <- read 1:address:channel
+    2:character, 1:address:channel <- read 1:address:channel
   ]
   memory-should-contain [
     2 <- 34
@@ -28,7 +28,7 @@ container channel [
   # A circular buffer contains values from index first-full up to (but not
   # including) index first-empty. The reader always modifies it at first-full,
   # while the writer always modifies it at first-empty.
-  data:address:array:location
+  data:address:array:character
 ]
 
 # result:address:channel <- new-channel capacity:number
@@ -45,16 +45,16 @@ recipe new-channel [
   # result.data = new location[ingredient+1]
   capacity:number <- next-ingredient
   capacity <- add capacity, 1  # unused slot for 'full?' below
-  dest:address:address:array:location <- get-address *result, data:offset
-  *dest <- new location:type, capacity
+  dest:address:address:array:character <- get-address *result, data:offset
+  *dest <- new character:type, capacity
   reply result
 ]
 
-# chan <- write chan:address:channel, val:location
+# chan <- write chan:address:channel, val:character
 recipe write [
   local-scope
   chan:address:channel <- next-ingredient
-  val:location <- next-ingredient
+  val:character <- next-ingredient
   {
     # block if chan is full
     full:boolean <- channel-full? chan
@@ -63,9 +63,9 @@ recipe write [
     wait-for-location *full-address
   }
   # store val
-  circular-buffer:address:array:location <- get *chan, data:offset
+  circular-buffer:address:array:character <- get *chan, data:offset
   free:address:number <- get-address *chan, first-free:offset
-  dest:address:location <- index-address *circular-buffer, *free
+  dest:address:character <- index-address *circular-buffer, *free
   *dest <- copy val
   # mark its slot as filled
   *free <- add *free, 1
@@ -79,7 +79,7 @@ recipe write [
   reply chan/same-as-ingredient:0
 ]
 
-# result:location, chan <- read chan:address:channel
+# result:character, chan <- read chan:address:channel
 recipe read [
   local-scope
   chan:address:channel <- next-ingredient
@@ -92,8 +92,8 @@ recipe read [
   }
   # read result
   full:address:number <- get-address *chan, first-full:offset
-  circular-buffer:address:array:location <- get *chan, data:offset
-  result:location <- index *circular-buffer, *full
+  circular-buffer:address:array:character <- get *chan, data:offset
+  result:character <- index *circular-buffer, *full
   # mark its slot as empty
   *full <- add *full, 1
   {
@@ -219,7 +219,7 @@ recipe channel-full? [
 recipe channel-capacity [
   local-scope
   chan:address:channel <- next-ingredient
-  q:address:array:location <- get *chan, data:offset
+  q:address:array:character <- get *chan, data:offset
   result:number <- length *q
   reply result
 ]
diff --git a/065duplex_list.mu b/065duplex_list.mu
index 15aa6a71..71ac00f5 100644
--- a/065duplex_list.mu
+++ b/065duplex_list.mu
@@ -59,19 +59,19 @@ scenario duplex-list-handling [
     3:address:duplex-list <- push-duplex 4, 3:address:duplex-list
     3:address:duplex-list <- push-duplex 5, 3:address:duplex-list
     4:address:duplex-list <- copy 3:address:duplex-list
-    5:number <- first-duplex 4:address:duplex-list
+    5:character <- first-duplex 4:address:duplex-list
     4:address:duplex-list <- next-duplex 4:address:duplex-list
-    6:number <- first-duplex 4:address:duplex-list
+    6:character <- first-duplex 4:address:duplex-list
     4:address:duplex-list <- next-duplex 4:address:duplex-list
-    7:number <- first-duplex 4:address:duplex-list
+    7:character <- first-duplex 4:address:duplex-list
     8:address:duplex-list <- next-duplex 4:address:duplex-list
-    9:number <- first-duplex 8:address:duplex-list
+    9:character <- first-duplex 8:address:duplex-list
     10:address:duplex-list <- next-duplex 8:address:duplex-list
     11:address:duplex-list <- prev-duplex 8:address:duplex-list
     4:address:duplex-list <- prev-duplex 4:address:duplex-list
-    12:number <- first-duplex 4:address:duplex-list
+    12:character <- first-duplex 4:address:duplex-list
     4:address:duplex-list <- prev-duplex 4:address:duplex-list
-    13:number <- first-duplex 4:address:duplex-list
+    13:character <- first-duplex 4:address:duplex-list
     14:boolean <- equal 3:address:duplex-list, 4:address:duplex-list
   ]
   memory-should-contain [
@@ -128,19 +128,19 @@ scenario inserting-into-duplex-list [
     2:address:duplex-list <- insert-duplex 6, 2:address:duplex-list
     # check structure like before
     2:address:duplex-list <- copy 1:address:duplex-list
-    3:number <- first-duplex 2:address:duplex-list
+    3:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- next-duplex 2:address:duplex-list
-    4:number <- first-duplex 2:address:duplex-list
+    4:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- next-duplex 2:address:duplex-list
-    5:number <- first-duplex 2:address:duplex-list
+    5:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- next-duplex 2:address:duplex-list
-    6:number <- first-duplex 2:address:duplex-list
+    6:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- prev-duplex 2:address:duplex-list
-    7:number <- first-duplex 2:address:duplex-list
+    7:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- prev-duplex 2:address:duplex-list
-    8:number <- first-duplex 2:address:duplex-list
+    8:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- prev-duplex 2:address:duplex-list
-    9:number <- first-duplex 2:address:duplex-list
+    9:character <- first-duplex 2:address:duplex-list
     10:boolean <- equal 1:address:duplex-list, 2:address:duplex-list
   ]
   memory-should-contain [
@@ -166,19 +166,19 @@ scenario inserting-at-end-of-duplex-list [
     2:address:duplex-list <- insert-duplex 6, 2:address:duplex-list
     # check structure like before
     2:address:duplex-list <- copy 1:address:duplex-list
-    3:number <- first-duplex 2:address:duplex-list
+    3:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- next-duplex 2:address:duplex-list
-    4:number <- first-duplex 2:address:duplex-list
+    4:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- next-duplex 2:address:duplex-list
-    5:number <- first-duplex 2:address:duplex-list
+    5:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- next-duplex 2:address:duplex-list
-    6:number <- first-duplex 2:address:duplex-list
+    6:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- prev-duplex 2:address:duplex-list
-    7:number <- first-duplex 2:address:duplex-list
+    7:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- prev-duplex 2:address:duplex-list
-    8:number <- first-duplex 2:address:duplex-list
+    8:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- prev-duplex 2:address:duplex-list
-    9:number <- first-duplex 2:address:duplex-list
+    9:character <- first-duplex 2:address:duplex-list
     10:boolean <- equal 1:address:duplex-list, 2:address:duplex-list
   ]
   memory-should-contain [
@@ -202,19 +202,19 @@ scenario inserting-after-start-of-duplex-list [
     2:address:duplex-list <- insert-duplex 6, 1:address:duplex-list
     # check structure like before
     2:address:duplex-list <- copy 1:address:duplex-list
-    3:number <- first-duplex 2:address:duplex-list
+    3:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- next-duplex 2:address:duplex-list
-    4:number <- first-duplex 2:address:duplex-list
+    4:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- next-duplex 2:address:duplex-list
-    5:number <- first-duplex 2:address:duplex-list
+    5:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- next-duplex 2:address:duplex-list
-    6:number <- first-duplex 2:address:duplex-list
+    6:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- prev-duplex 2:address:duplex-list
-    7:number <- first-duplex 2:address:duplex-list
+    7:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- prev-duplex 2:address:duplex-list
-    8:number <- first-duplex 2:address:duplex-list
+    8:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- prev-duplex 2:address:duplex-list
-    9:number <- first-duplex 2:address:duplex-list
+    9:character <- first-duplex 2:address:duplex-list
     10:boolean <- equal 1:address:duplex-list, 2:address:duplex-list
   ]
   memory-should-contain [
@@ -276,12 +276,12 @@ scenario removing-from-duplex-list [
     3:boolean <- equal 2:address:duplex-list, 0
     # check structure like before
     2:address:duplex-list <- copy 1:address:duplex-list
-    4:number <- first-duplex 2:address:duplex-list
+    4:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- next-duplex 2:address:duplex-list
-    5:number <- first-duplex 2:address:duplex-list
+    5:character <- first-duplex 2:address:duplex-list
     6:address:duplex-list <- next-duplex 2:address:duplex-list
     2:address:duplex-list <- prev-duplex 2:address:duplex-list
-    7:number <- first-duplex 2:address:duplex-list
+    7:character <- first-duplex 2:address:duplex-list
     8:boolean <- equal 1:address:duplex-list, 2:address:duplex-list
   ]
   memory-should-contain [
@@ -304,12 +304,12 @@ scenario removing-from-start-of-duplex-list [
     1:address:duplex-list <- remove-duplex 1:address:duplex-list
     # check structure like before
     2:address:duplex-list <- copy 1:address:duplex-list
-    3:number <- first-duplex 2:address:duplex-list
+    3:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- next-duplex 2:address:duplex-list
-    4:number <- first-duplex 2:address:duplex-list
+    4:character <- first-duplex 2:address:duplex-list
     5:address:duplex-list <- next-duplex 2:address:duplex-list
     2:address:duplex-list <- prev-duplex 2:address:duplex-list
-    6:number <- first-duplex 2:address:duplex-list
+    6:character <- first-duplex 2:address:duplex-list
     7:boolean <- equal 1:address:duplex-list, 2:address:duplex-list
   ]
   memory-should-contain [
@@ -334,12 +334,12 @@ scenario removing-from-end-of-duplex-list [
     3:boolean <- equal 2:address:duplex-list, 0
     # check structure like before
     2:address:duplex-list <- copy 1:address:duplex-list
-    4:number <- first-duplex 2:address:duplex-list
+    4:character <- first-duplex 2:address:duplex-list
     2:address:duplex-list <- next-duplex 2:address:duplex-list
-    5:number <- first-duplex 2:address:duplex-list
+    5:character <- first-duplex 2:address:duplex-list
     6:address:duplex-list <- next-duplex 2:address:duplex-list
     2:address:duplex-list <- prev-duplex 2:address:duplex-list
-    7:number <- first-duplex 2:address:duplex-list
+    7:character <- first-duplex 2:address:duplex-list
     8:boolean <- equal 1:address:duplex-list, 2:address:duplex-list
   ]
   memory-should-contain [
diff --git a/071print.mu b/071print.mu
index 2789a0ed..d1cea5cb 100644
--- a/071print.mu
+++ b/071print.mu
@@ -614,7 +614,7 @@ recipe show-screen [
 
 recipe print-string [
   local-scope
-  screen:address <- next-ingredient
+  screen:address:screen <- next-ingredient
   s:address:array:character <- next-ingredient
   color:number, color-found?:boolean <- next-ingredient
   {
diff --git a/081run_interactive.cc b/081run_interactive.cc
index 8548f568..224998ec 100644
--- a/081run_interactive.cc
+++ b/081run_interactive.cc
@@ -86,7 +86,7 @@ bool run_interactive(long long int address) {
   // call run(string) but without the scheduling
   load(string("recipe interactive [\n") +
           "local-scope\n" +
-          "screen:address <- next-ingredient\n" +
+          "screen:address:screen <- next-ingredient\n" +
           "$start-tracking-products\n" +
           command + "\n" +
           "$stop-tracking-products\n" +
@@ -129,8 +129,8 @@ load(string(
 "]\n" +
 "recipe sandbox [\n" +
   "local-scope\n" +
-  "screen:address/shared <- new-fake-screen 30, 5\n" +
-  "r:number/routine_id <- start-running interactive:recipe, screen:address\n" +
+  "screen:address:screen/shared <- new-fake-screen 30, 5\n" +
+  "r:number/routine_id <- start-running interactive:recipe, screen\n" +
   "limit-time r, 100000/instructions\n" +
   "wait-for-routine r\n" +
   "sandbox-state:number <- routine-state r/routine_id\n" +
diff --git a/channel.mu b/channel.mu
index ffa6d9f6..bc6ee44c 100644
--- a/channel.mu
+++ b/channel.mu
@@ -1,11 +1,11 @@
 # example program: communicating between routines using channels
 
 recipe producer [
-  # produce numbers 1 to 5 on a channel
+  # produce characters 1 to 5 on a channel
   local-scope
   chan:address:channel <- next-ingredient
   # n = 0
-  n:number <- copy 0
+  n:character <- copy 0
   {
     done?:boolean <- lesser-than n, 5
     break-unless done?
@@ -24,9 +24,9 @@ recipe consumer [
   chan:address:channel <- next-ingredient
   {
     # read an integer from the channel
-    n:number, chan:address:channel <- read chan
+    n:character, chan:address:channel <- read chan
     # other threads might get between these prints
-    $print [consume: ], n:number, [ 
+    $print [consume: ], n:character, [ 
 ]
     loop
   }
@@ -36,8 +36,8 @@ recipe main [
   local-scope
   chan:address:channel <- new-channel 3
   # create two background 'routines' that communicate by a channel
-  routine1:number <- start-running producer:recipe, chan
-  routine2:number <- start-running consumer:recipe, chan
+  routine1:character <- start-running producer:recipe, chan
+  routine2:character <- start-running consumer:recipe, chan
   wait-for-routine routine1
   wait-for-routine routine2
 ]
diff --git a/edit/005-sandbox.mu b/edit/005-sandbox.mu
index 4aad213b..c255865a 100644
--- a/edit/005-sandbox.mu
+++ b/edit/005-sandbox.mu
@@ -458,7 +458,7 @@ scenario run-instruction-manages-screen-per-sandbox [
   # left editor is empty
   1:address:array:character <- new []
   # right editor contains an instruction
-  2:address:array:character <- new [print-integer screen:address, 4]
+  2:address:array:character <- new [print-integer screen, 4]
   3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
   # run the code in the editor
   assume-console [
@@ -473,7 +473,7 @@ scenario run-instruction-manages-screen-per-sandbox [
     .                                                  ┊                                                 .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
     .                                                  ┊                                                x.
-    .                                                  ┊print-integer screen:address, 4                  .
+    .                                                  ┊print-integer screen, 4                          .
     .                                                  ┊screen:                                          .
     .                                                  ┊  .4                             .               .
     .                                                  ┊  .                              .               .
diff --git a/edit/006-sandbox-edit.mu b/edit/006-sandbox-edit.mu
index 90be2de7..861d9bcd 100644
--- a/edit/006-sandbox-edit.mu
+++ b/edit/006-sandbox-edit.mu
@@ -132,7 +132,7 @@ scenario sandbox-with-print-can-be-edited [
   # left editor is empty
   1:address:array:character <- new []
   # right editor contains an instruction
-  2:address:array:character <- new [print-integer screen:address, 4]
+  2:address:array:character <- new [print-integer screen, 4]
   3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
   # run the sandbox
   assume-console [
@@ -144,7 +144,7 @@ scenario sandbox-with-print-can-be-edited [
     .                                                  ┊                                                 .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
     .                                                  ┊                                                x.
-    .                                                  ┊print-integer screen:address, 4                  .
+    .                                                  ┊print-integer screen, 4                          .
     .                                                  ┊screen:                                          .
     .                                                  ┊  .4                             .               .
     .                                                  ┊  .                              .               .
@@ -163,7 +163,7 @@ scenario sandbox-with-print-can-be-edited [
   ]
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊print-integer screen:address, 4                  .
+    .                                                  ┊print-integer screen, 4                          .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
     .                                                  ┊                                                 .
     .                                                  ┊                                                 .
diff --git a/edit/011-editor-undo.mu b/edit/011-editor-undo.mu
index 217cbd20..fe44110f 100644
--- a/edit/011-editor-undo.mu
+++ b/edit/011-editor-undo.mu
@@ -67,10 +67,10 @@ after <handle-special-character> [
     break-unless undo?
     undo:address:address:list <- get-address *editor, undo:offset
     break-unless *undo
-    op:address:operation <- first *undo
+    op:address:operation/skiptypecheck <- first *undo
     *undo <- rest *undo
     redo:address:address:list <- get-address *editor, redo:offset
-    *redo <- push op, *redo
+    *redo/skiptypecheck <- push op, *redo
     <handle-undo>
     reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render
   }
@@ -83,10 +83,10 @@ after <handle-special-character> [
     break-unless redo?
     redo:address:address:list <- get-address *editor, redo:offset
     break-unless *redo
-    op:address:operation <- first *redo
+    op:address:operation/skiptypecheck <- first *redo
     *redo <- rest *redo
     undo:address:address:list <- get-address *editor, undo:offset
-    *undo <- push op, *undo
+    *undo/skiptypecheck <- push op, *undo
     <handle-redo>
     reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render
   }
@@ -144,7 +144,7 @@ before <insert-character-end> [
   {
     # if previous operation was an insert, coalesce this operation with it
     break-unless *undo
-    op:address:operation <- first *undo
+    op:address:operation/skiptypecheck <- first *undo
     typing:address:insert-operation <- maybe-convert *op, typing:variant
     break-unless typing
     previous-coalesce-tag:number <- get *typing, tag:offset
@@ -194,7 +194,7 @@ recipe add-operation [
   editor:address:editor-data <- next-ingredient
   op:address:operation <- next-ingredient
   undo:address:address:list:address:operation <- get-address *editor, undo:offset
-  *undo <- push op *undo
+  *undo/skiptypecheck <- push op *undo
   redo:address:address:list:address:operation <- get-address *editor, redo:offset
   *redo <- copy 0
   reply editor/same-as-ingredient:0
@@ -715,7 +715,7 @@ before <move-cursor-end> [
     # tag, coalesce with it
     undo:address:address:list <- get-address *editor, undo:offset
     break-unless *undo
-    op:address:operation <- first *undo
+    op:address:operation/skiptypecheck <- first *undo
     move:address:move-operation <- maybe-convert *op, move:variant
     break-unless move
     previous-coalesce-tag:number <- get *move, tag:offset
@@ -1600,7 +1600,7 @@ before <backspace-character-end> [
     {
       # if previous operation was an insert, coalesce this operation with it
       break-unless *undo
-      op:address:operation <- first *undo
+      op:address:operation/skiptypecheck <- first *undo
       deletion:address:delete-operation <- maybe-convert *op, delete:variant
       break-unless deletion
       previous-coalesce-tag:number <- get *deletion, tag:offset
@@ -1822,7 +1822,7 @@ before <delete-character-end> [
     {
       # if previous operation was an insert, coalesce this operation with it
       break-unless *undo
-      op:address:operation <- first *undo
+      op:address:operation/skiptypecheck <- first *undo
       deletion:address:delete-operation <- maybe-convert *op, delete:variant
       break-unless deletion
       previous-coalesce-tag:number <- get *deletion, tag:offset
diff --git a/sandbox/005-sandbox.mu b/sandbox/005-sandbox.mu
index b4a6b013..a06f3076 100644
--- a/sandbox/005-sandbox.mu
+++ b/sandbox/005-sandbox.mu
@@ -391,7 +391,7 @@ scenario run-instruction-manages-screen-per-sandbox [
   $close-trace  # trace too long
   assume-screen 50/width, 20/height
   # editor contains an instruction
-  1:address:array:character <- new [print-integer screen:address, 4]
+  1:address:array:character <- new [print-integer screen, 4]
   2:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character
   # run the code in the editor
   assume-console [
@@ -406,7 +406,7 @@ scenario run-instruction-manages-screen-per-sandbox [
     .                                                  .
     .━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
     .                                                 x.
-    .print-integer screen:address, 4                   .
+    .print-integer screen, 4                           .
     .screen:                                           .
     .  .4                             .                .
     .  .                              .                .
diff --git a/sandbox/006-sandbox-edit.mu b/sandbox/006-sandbox-edit.mu
index 15872675..5a628f3a 100644
--- a/sandbox/006-sandbox-edit.mu
+++ b/sandbox/006-sandbox-edit.mu
@@ -128,7 +128,7 @@ scenario sandbox-with-print-can-be-edited [
   $close-trace  # trace too long
   assume-screen 50/width, 20/height
   # run a print instruction
-  1:address:array:character <- new [print-integer screen:address, 4]
+  1:address:array:character <- new [print-integer screen, 4]
   2:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character
   # run the sandbox
   assume-console [
@@ -142,7 +142,7 @@ scenario sandbox-with-print-can-be-edited [
     .                                                  .
     .━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
     .                                                 x.
-    .print-integer screen:address, 4                   .
+    .print-integer screen, 4                           .
     .screen:                                           .
     .  .4                             .                .
     .  .                              .                .
@@ -168,7 +168,7 @@ scenario sandbox-with-print-can-be-edited [
   ]
   screen-should-contain [
     .                               run (F4)           .
-    .print-integer screen:address, 4                   .
+    .print-integer screen, 4                           .
     .━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
     .                                                  .
     .                                                  .
diff --git a/sandbox/011-editor-undo.mu b/sandbox/011-editor-undo.mu
index 217cbd20..fe44110f 100644
--- a/sandbox/011-editor-undo.mu
+++ b/sandbox/011-editor-undo.mu
@@ -67,10 +67,10 @@ after <handle-special-character> [
     break-unless undo?
     undo:address:address:list <- get-address *editor, undo:offset
     break-unless *undo
-    op:address:operation <- first *undo
+    op:address:operation/skiptypecheck <- first *undo
     *undo <- rest *undo
     redo:address:address:list <- get-address *editor, redo:offset
-    *redo <- push op, *redo
+    *redo/skiptypecheck <- push op, *redo
     <handle-undo>
     reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render
   }
@@ -83,10 +83,10 @@ after <handle-special-character> [
     break-unless redo?
     redo:address:address:list <- get-address *editor, redo:offset
     break-unless *redo
-    op:address:operation <- first *redo
+    op:address:operation/skiptypecheck <- first *redo
     *redo <- rest *redo
     undo:address:address:list <- get-address *editor, undo:offset
-    *undo <- push op, *undo
+    *undo/skiptypecheck <- push op, *undo
     <handle-redo>
     reply screen/same-as-ingredient:0, editor/same-as-ingredient:1, 1/go-render
   }
@@ -144,7 +144,7 @@ before <insert-character-end> [
   {
     # if previous operation was an insert, coalesce this operation with it
     break-unless *undo
-    op:address:operation <- first *undo
+    op:address:operation/skiptypecheck <- first *undo
     typing:address:insert-operation <- maybe-convert *op, typing:variant
     break-unless typing
     previous-coalesce-tag:number <- get *typing, tag:offset
@@ -194,7 +194,7 @@ recipe add-operation [
   editor:address:editor-data <- next-ingredient
   op:address:operation <- next-ingredient
   undo:address:address:list:address:operation <- get-address *editor, undo:offset
-  *undo <- push op *undo
+  *undo/skiptypecheck <- push op *undo
   redo:address:address:list:address:operation <- get-address *editor, redo:offset
   *redo <- copy 0
   reply editor/same-as-ingredient:0
@@ -715,7 +715,7 @@ before <move-cursor-end> [
     # tag, coalesce with it
     undo:address:address:list <- get-address *editor, undo:offset
     break-unless *undo
-    op:address:operation <- first *undo
+    op:address:operation/skiptypecheck <- first *undo
     move:address:move-operation <- maybe-convert *op, move:variant
     break-unless move
     previous-coalesce-tag:number <- get *move, tag:offset
@@ -1600,7 +1600,7 @@ before <backspace-character-end> [
     {
       # if previous operation was an insert, coalesce this operation with it
       break-unless *undo
-      op:address:operation <- first *undo
+      op:address:operation/skiptypecheck <- first *undo
       deletion:address:delete-operation <- maybe-convert *op, delete:variant
       break-unless deletion
       previous-coalesce-tag:number <- get *deletion, tag:offset
@@ -1822,7 +1822,7 @@ before <delete-character-end> [
     {
       # if previous operation was an insert, coalesce this operation with it
       break-unless *undo
-      op:address:operation <- first *undo
+      op:address:operation/skiptypecheck <- first *undo
       deletion:address:delete-operation <- maybe-convert *op, delete:variant
       break-unless deletion
       previous-coalesce-tag:number <- get *deletion, tag:offset