about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--056recipe_header.cc4
-rw-r--r--070string.mu205
-rw-r--r--071channel.mu56
-rw-r--r--076stream.mu27
-rw-r--r--081print.mu243
-rw-r--r--084console.mu47
-rw-r--r--channel.mu8
-rw-r--r--chessboard.mu74
-rw-r--r--edit/001-editor.mu6
9 files changed, 283 insertions, 387 deletions
diff --git a/056recipe_header.cc b/056recipe_header.cc
index c7b361d6..e918074d 100644
--- a/056recipe_header.cc
+++ b/056recipe_header.cc
@@ -145,9 +145,7 @@ void check_reply_instructions_against_header(const recipe_ordinal r) {
   for (long long int i = 0; i < SIZE(caller_recipe.steps); ++i) {
     const instruction& inst = caller_recipe.steps.at(i);
     if (inst.name != "reply") continue;
-    if (SIZE(caller_recipe.products) != SIZE(inst.ingredients))
-      raise_error << maybe(caller_recipe.name) << "tried to reply the wrong number of products in '" << inst.to_string() << "'\n" << end();
-    for (long long int i = 0; i < SIZE(caller_recipe.products); ++i) {
+    for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
       if (!types_match(caller_recipe.products.at(i), inst.ingredients.at(i)))
         raise_error << maybe(caller_recipe.name) << "replied with the wrong type at '" << inst.to_string() << "'\n" << end();
     }
diff --git a/070string.mu b/070string.mu
index 854e27a9..640a85c8 100644
--- a/070string.mu
+++ b/070string.mu
@@ -1,10 +1,9 @@
 # Some useful helpers for dealing with strings.
 
-recipe string-equal [
+recipe string-equal a:address:array:character, b:address:array:character -> result:boolean [
   local-scope
-  a:address:array:character <- next-ingredient
+  load-ingredients
   a-len:number <- length *a
-  b:address:array:character <- next-ingredient
   b-len:number <- length *b
   # compare lengths
   {
@@ -103,21 +102,20 @@ container buffer [
   data:address:array:character
 ]
 
-recipe new-buffer [
+recipe new-buffer capacity:number -> result:address:buffer [
   local-scope
-  result:address:buffer <- new buffer:type
+  load-ingredients
+  result <- new buffer:type
   len:address:number <- get-address *result, length:offset
   *len:address:number <- copy 0
   s:address:address:array:character <- get-address *result, data:offset
-  capacity:number, found?:boolean <- next-ingredient
-  assert found?, [new-buffer must get a capacity argument]
   *s <- new character:type, capacity
   reply result
 ]
 
-recipe grow-buffer [
+recipe grow-buffer in:address:buffer -> in:address:buffer [
   local-scope
-  in:address:buffer <- next-ingredient
+  load-ingredients
   # double buffer size
   x:address:address:array:character <- get-address *in, data:offset
   oldlen:number <- length **x
@@ -135,33 +133,29 @@ recipe grow-buffer [
     i <- add i, 1
     loop
   }
-  reply in
 ]
 
-recipe buffer-full? [
+recipe buffer-full? in:address:buffer -> result:boolean [
   local-scope
-  in:address:buffer <- next-ingredient
+  load-ingredients
   len:number <- get *in, length:offset
   s:address:array:character <- get *in, data:offset
   capacity:number <- length *s
-  result:boolean <- greater-or-equal len, capacity
-  reply result
+  result <- greater-or-equal len, capacity
 ]
 
-# in <- buffer-append in:address:buffer, c:character
-recipe buffer-append [
+recipe buffer-append in:address:buffer, c:character -> in:address:buffer [
   local-scope
-  in:address:buffer <- next-ingredient
-  c:character <- next-ingredient
+  load-ingredients
   len:address:number <- get-address *in, length:offset
   {
     # backspace? just drop last character if it exists and return
     backspace?:boolean <- equal c, 8/backspace
     break-unless backspace?
     empty?:boolean <- lesser-or-equal *len, 0
-    reply-if empty?, in/same-as-ingredient:0
+    reply-if empty?
     *len <- subtract *len, 1
-    reply in/same-as-ingredient:0
+    reply
   }
   {
     # grow buffer if necessary
@@ -173,7 +167,6 @@ recipe buffer-append [
   dest:address:character <- index-address *s, *len
   *dest <- copy c
   *len <- add *len, 1
-  reply in/same-as-ingredient:0
 ]
 
 scenario buffer-append-works [
@@ -232,14 +225,14 @@ scenario buffer-append-handles-backspace [
 ]
 
 # result:address:array:character <- integer-to-decimal-string n:number
-recipe integer-to-decimal-string [
+recipe integer-to-decimal-string n:number -> result:address:array:character [
   local-scope
-  n:number <- next-ingredient
-  # is it zero?
+  load-ingredients
+  # is n zero?
   {
     break-if n
-    result:address:array:character <- new [0]
-    reply result
+    result <- new [0]
+    reply
   }
   # save sign
   negate-result:boolean <- copy 0
@@ -268,7 +261,7 @@ recipe integer-to-decimal-string [
   # reverse buffer into string result
   len:number <- get *tmp, length:offset
   buf:address:array:character <- get *tmp, data:offset
-  result:address:array:character <- new character:type, len
+  result <- new character:type, len
   i:number <- subtract len, 1  # source index, decreasing
   j:number <- copy 0  # destination index, increasing
   {
@@ -283,12 +276,11 @@ recipe integer-to-decimal-string [
     j <- add j, 1
     loop
   }
-  reply result
 ]
 
-recipe buffer-to-array [
+recipe buffer-to-array in:address:buffer -> result:address:array:character [
   local-scope
-  in:address:buffer <- next-ingredient
+  load-ingredients
   {
     # propagate null buffer
     break-if in
@@ -297,7 +289,7 @@ recipe buffer-to-array [
   len:number <- get *in, length:offset
   s:address:array:character <- get *in, data:offset
   # we can't just return s because it is usually the wrong length
-  result:address:array:character <- new character:type, len
+  result <- new character:type, len
   i:number <- copy 0
   {
     done?:boolean <- greater-or-equal i, len
@@ -308,7 +300,6 @@ recipe buffer-to-array [
     i <- add i, 1
     loop
   }
-  reply result
 ]
 
 scenario integer-to-decimal-digit-zero [
@@ -343,16 +334,14 @@ scenario integer-to-decimal-digit-negative [
   ]
 ]
 
-# result:address:array:character <- string-append a:address:array:character, b:address:array:character
-recipe string-append [
+recipe string-append a:address:array:character, b:address:array:character -> result:address:array:character [
   local-scope
+  load-ingredients
   # result = new character[a.length + b.length]
-  a:address:array:character <- next-ingredient
   a-len:number <- length *a
-  b:address:array:character <- next-ingredient
   b-len:number <- length *b
   result-len:number <- add a-len, b-len
-  result:address:array:character <- new character:type, result-len
+  result <- new character:type, result-len
   # copy a into result
   result-idx:number <- copy 0
   i:number <- copy 0
@@ -382,7 +371,6 @@ recipe string-append [
     result-idx <- add result-idx, 1
     loop
   }
-  reply result
 ]
 
 scenario string-append-1 [
@@ -408,11 +396,9 @@ scenario replace-character-in-string [
   ]
 ]
 
-recipe string-replace [
+recipe string-replace s:address:array:character, oldc:character, newc:character -> s:address:array:character [
   local-scope
-  s:address:array:character <- next-ingredient
-  oldc:character <- next-ingredient
-  newc:character <- next-ingredient
+  load-ingredients
   from:number, _ <- next-ingredient  # default to 0
   len:number <- length *s
   i:number <- find-next s, oldc, from
@@ -422,7 +408,6 @@ recipe string-replace [
   *dest <- copy newc
   i <- add i, 1
   s <- string-replace s, oldc, newc, i
-  reply s/same-as-ingredient:0
 ]
 
 scenario replace-character-at-start [
@@ -470,15 +455,14 @@ scenario replace-all-characters [
 ]
 
 # replace underscores in first with remaining args
-# result:address:array:character <- interpolate template:address:array:character, ...
-recipe interpolate [
+recipe interpolate template:address:array:character -> result:address:array:character [
   local-scope
-  template:address:array:character <- next-ingredient
+  load-ingredients  # consume just the template
   # compute result-len, space to allocate for result
   tem-len:number <- length *template
   result-len:number <- copy tem-len
   {
-    # while arg received
+    # while ingredients remain
     a:address:array:character, arg-received?:boolean <- next-ingredient
     break-unless arg-received?
     # result-len = result-len + arg.length - 1 (for the 'underscore' being replaced)
@@ -545,7 +529,6 @@ recipe interpolate [
     result-idx <- add result-idx, 1
     loop
   }
-  reply result
 ]
 
 scenario interpolate-works [
@@ -586,72 +569,70 @@ scenario interpolate-at-end [
 ]
 
 # result:boolean <- space? c:character
-recipe space? [
+recipe space? c:character -> result:boolean [
   local-scope
-  c:character <- next-ingredient
+  load-ingredients
   # most common case first
-  result:boolean <- equal c, 32/space
-  reply-if result, result
+  result <- equal c, 32/space
+  reply-if result
   result <- equal c, 10/newline
-  reply-if result, result
+  reply-if result
   result <- equal c, 9/tab
-  reply-if result, result
+  reply-if result
   result <- equal c, 13/carriage-return
-  reply-if result, result
+  reply-if result
   # remaining uncommon cases in sorted order
   # http://unicode.org code-points in unicode-set Z and Pattern_White_Space
   result <- equal c, 11/ctrl-k
-  reply-if result, result
+  reply-if result
   result <- equal c, 12/ctrl-l
-  reply-if result, result
+  reply-if result
   result <- equal c, 133/ctrl-0085
-  reply-if result, result
+  reply-if result
   result <- equal c, 160/no-break-space
-  reply-if result, result
+  reply-if result
   result <- equal c, 5760/ogham-space-mark
-  reply-if result, result
+  reply-if result
   result <- equal c, 8192/en-quad
-  reply-if result, result
+  reply-if result
   result <- equal c, 8193/em-quad
-  reply-if result, result
+  reply-if result
   result <- equal c, 8194/en-space
-  reply-if result, result
+  reply-if result
   result <- equal c, 8195/em-space
-  reply-if result, result
+  reply-if result
   result <- equal c, 8196/three-per-em-space
-  reply-if result, result
+  reply-if result
   result <- equal c, 8197/four-per-em-space
-  reply-if result, result
+  reply-if result
   result <- equal c, 8198/six-per-em-space
-  reply-if result, result
+  reply-if result
   result <- equal c, 8199/figure-space
-  reply-if result, result
+  reply-if result
   result <- equal c, 8200/punctuation-space
-  reply-if result, result
+  reply-if result
   result <- equal c, 8201/thin-space
-  reply-if result, result
+  reply-if result
   result <- equal c, 8202/hair-space
-  reply-if result, result
+  reply-if result
   result <- equal c, 8206/left-to-right
-  reply-if result, result
+  reply-if result
   result <- equal c, 8207/right-to-left
-  reply-if result, result
+  reply-if result
   result <- equal c, 8232/line-separator
-  reply-if result, result
+  reply-if result
   result <- equal c, 8233/paragraph-separator
-  reply-if result, result
+  reply-if result
   result <- equal c, 8239/narrow-no-break-space
-  reply-if result, result
+  reply-if result
   result <- equal c, 8287/medium-mathematical-space
-  reply-if result, result
+  reply-if result
   result <- equal c, 12288/ideographic-space
-  reply result
 ]
 
-# result:address:array:character <- trim s:address:array:character
-recipe trim [
+recipe trim s:address:array:character -> result:address:array:character [
   local-scope
-  s:address:array:character <- next-ingredient
+  load-ingredients
   len:number <- length *s
   # left trim: compute start
   start:number <- copy 0
@@ -659,8 +640,8 @@ recipe trim [
     {
       at-end?:boolean <- greater-or-equal start, len
       break-unless at-end?
-      result:address:array:character <- new character:type, 0
-      reply result
+      result <- new character:type, 0
+      reply
     }
     curr:character <- index *s, start
     whitespace?:boolean <- space? curr
@@ -697,7 +678,6 @@ recipe trim [
     j <- add j, 1
     loop
   }
-  reply result
 ]
 
 scenario trim-unmodified [
@@ -756,8 +736,7 @@ scenario trim-newline-tab [
   ]
 ]
 
-# next-index:number <- find-next text:address:array:character, pattern:character, idx:number
-recipe find-next [
+recipe find-next text:address:array:character, pattern:character, idx:number -> next-index:number [
   local-scope
   text:address:array:character <- next-ingredient
   pattern:character <- next-ingredient
@@ -855,14 +834,11 @@ scenario string-find-next-second [
   ]
 ]
 
-# next-index:number <- find-substring text:address:array:character, pattern:address:array:character, idx:number
 # like find-next, but searches for multiple characters
 # fairly dumb algorithm
-recipe find-substring [
+recipe find-substring text:address:array:character, pattern:address:array:character, idx:number -> next-idx:number [
   local-scope
-  text:address:array:character <- next-ingredient
-  pattern:address:array:character <- next-ingredient
-  idx:number <- next-ingredient
+  load-ingredients
   first:character <- index *pattern, 0
   # repeatedly check for match at current idx
   len:number <- length *text
@@ -935,13 +911,10 @@ scenario find-substring-suffix-match-2 [
   ]
 ]
 
-# result:boolean <- match-at text:address:array:character, pattern:address:array:character, idx:number
 # checks if substring matches at index 'idx'
-recipe match-at [
+recipe match-at text:address:array:character, pattern:address:array:character, idx:number -> result:boolean [
   local-scope
-  text:address:array:character <- next-ingredient
-  pattern:address:array:character <- next-ingredient
-  idx:number <- next-ingredient
+  load-ingredients
   pattern-len:number <- length *pattern
   # check that there's space left for the pattern
   {
@@ -1067,18 +1040,16 @@ scenario match-at-inside-bounds-2 [
   ]
 ]
 
-# result:address:array:address:array:character <- split s:address:array:character, delim:character
-recipe split [
+recipe split s:address:array:character, delim:character -> result:address:array:address:array:character [
   local-scope
-  s:address:array:character <- next-ingredient
-  delim:character <- next-ingredient
+  load-ingredients
   # empty string? return empty array
   len:number <- length *s
   {
     empty?:boolean <- equal len, 0
     break-unless empty?
-    result:address:array:address:array:character <- new location:type, 0
-    reply result
+    result <- new location:type, 0
+    reply
   }
   # count #pieces we need room for
   count:number <- copy 1  # n delimiters = n+1 pieces
@@ -1092,7 +1063,7 @@ recipe split [
     loop
   }
   # allocate space
-  result:address:array:address:array:character <- new location:type, count
+  result <- new location:type, count
   # repeatedly copy slices start..end until delimiter into result[curr-result]
   curr-result:number <- copy 0
   start:number <- copy 0
@@ -1109,7 +1080,6 @@ recipe split [
     curr-result <- add curr-result, 1
     loop
   }
-  reply result
 ]
 
 scenario string-split-1 [
@@ -1197,11 +1167,9 @@ scenario string-split-empty-piece [
   ]
 ]
 
-# x:address:array:character, y:address:array:character <- split-first text:address:array:character, delim:character
-recipe split-first [
+recipe split-first text:address:array:character, delim:character -> x:address:array:character, y:address:array:character [
   local-scope
-  text:address:array:character <- next-ingredient
-  delim:character <- next-ingredient
+  load-ingredients
   # empty string? return empty strings
   len:number <- length *text
   {
@@ -1209,13 +1177,12 @@ recipe split-first [
     break-unless empty?
     x:address:array:character <- new []
     y:address:array:character <- new []
-    reply x, y
+    reply
   }
   idx:number <- find-next text, delim, 0
   x:address:array:character <- string-copy text, 0, idx
   idx <- add idx, 1
   y:address:array:character <- string-copy text, idx, len
-  reply x, y
 ]
 
 scenario string-split-first [
@@ -1231,13 +1198,10 @@ scenario string-split-first [
   ]
 ]
 
-# result:address:array:character <- string-copy buf:address:array:character, start:number, end:number
 # todo: make this generic
-recipe string-copy [
+recipe string-copy buf:address:array:character, start:number, end:number -> result:address:array:character [
   local-scope
-  buf:address:array:character <- next-ingredient
-  start:number <- next-ingredient
-  end:number <- next-ingredient
+  load-ingredients
   # if end is out of bounds, trim it
   len:number <- length *buf
   end:number <- min len, end
@@ -1257,7 +1221,6 @@ recipe string-copy [
     dest-idx <- add dest-idx, 1
     loop
   }
-  reply result
 ]
 
 scenario string-copy-copies-substring [
@@ -1293,10 +1256,9 @@ scenario string-copy-out-of-bounds-2 [
   ]
 ]
 
-recipe min [
+recipe min x:number, y:number -> z:number [
   local-scope
-  x:number <- next-ingredient
-  y:number <- next-ingredient
+  load-ingredients
   {
     return-x?:boolean <- lesser-than x, y
     break-if return-x?
@@ -1305,10 +1267,9 @@ recipe min [
   reply x
 ]
 
-recipe max [
+recipe max x:number, y:number -> z:number [
   local-scope
-  x:number <- next-ingredient
-  y:number <- next-ingredient
+  load-ingredients
   {
     return-x?:boolean <- greater-than x, y
     break-if return-x?
diff --git a/071channel.mu b/071channel.mu
index c8ca6cc9..d0dc3f88 100644
--- a/071channel.mu
+++ b/071channel.mu
@@ -32,10 +32,10 @@ container channel [
 ]
 
 # result:address:channel <- new-channel capacity:number
-recipe new-channel [
+recipe new-channel capacity:number -> result:address:channel [
   local-scope
-  # result = new channel
-  result:address:channel <- new channel:type
+  load-ingredients
+  result <- new channel:type
   # result.first-full = 0
   full:address:number <- get-address *result, first-full:offset
   *full <- copy 0
@@ -43,18 +43,14 @@ recipe new-channel [
   free:address:number <- get-address *result, first-free:offset
   *free <- copy 0
   # result.data = new location[ingredient+1]
-  capacity:number <- next-ingredient
   capacity <- add capacity, 1  # unused slot for 'full?' below
   dest:address:address:array:character <- get-address *result, data:offset
   *dest <- new character:type, capacity
-  reply result
 ]
 
-# chan <- write chan:address:channel, val:character
-recipe write [
+recipe write chan:address:channel, val:character -> chan:address:channel [
   local-scope
-  chan:address:channel <- next-ingredient
-  val:character <- next-ingredient
+  load-ingredients
   {
     # block if chan is full
     full:boolean <- channel-full? chan
@@ -76,13 +72,11 @@ recipe write [
     break-unless at-end?
     *free <- copy 0
   }
-  reply chan/same-as-ingredient:0
 ]
 
-# result:character, chan <- read chan:address:channel
-recipe read [
+recipe read chan:address:channel -> result:character, chan:address:channel [
   local-scope
-  chan:address:channel <- next-ingredient
+  load-ingredients
   {
     # block if chan is empty
     empty?:boolean <- channel-empty? chan
@@ -93,7 +87,7 @@ recipe read [
   # read result
   full:address:number <- get-address *chan, first-full:offset
   circular-buffer:address:array:character <- get *chan, data:offset
-  result:character <- index *circular-buffer, *full
+  result <- index *circular-buffer, *full
   # mark its slot as empty
   *full <- add *full, 1
   {
@@ -103,18 +97,16 @@ recipe read [
     break-unless at-end?
     *full <- copy 0
   }
-  reply result, chan/same-as-ingredient:0
 ]
 
-recipe clear-channel [
+recipe clear-channel chan:address:channel -> chan:address:channel [
   local-scope
-  chan:address:channel <- next-ingredient
+  load-ingredients
   {
     empty?:boolean <- channel-empty? chan
     break-if empty?
     _, chan <- read chan
   }
-  reply chan/same-as-ingredient:0
 ]
 
 scenario channel-initialization [
@@ -184,21 +176,20 @@ scenario channel-wrap [
 ## helpers
 
 # An empty channel has first-empty and first-full both at the same value.
-recipe channel-empty? [
+recipe channel-empty? chan:address:channel -> result:boolean [
   local-scope
-  chan:address:channel <- next-ingredient
+  load-ingredients
   # return chan.first-full == chan.first-free
   full:number <- get *chan, first-full:offset
   free:number <- get *chan, first-free:offset
-  result:boolean <- equal full, free
-  reply result
+  result <- equal full, free
 ]
 
 # A full channel has first-empty just before first-full, wasting one slot.
 # (Other alternatives: https://en.wikipedia.org/wiki/Circular_buffer#Full_.2F_Empty_Buffer_Distinction)
-recipe channel-full? [
+recipe channel-full? chan:address:channel -> result:boolean [
   local-scope
-  chan:address:channel <- next-ingredient
+  load-ingredients
   # tmp = chan.first-free + 1
   tmp:number <- get *chan, first-free:offset
   tmp <- add tmp, 1
@@ -211,17 +202,15 @@ recipe channel-full? [
   }
   # return chan.first-full == tmp
   full:number <- get *chan, first-full:offset
-  result:boolean <- equal full, tmp
-  reply result
+  result <- equal full, tmp
 ]
 
 # result:number <- channel-capacity chan:address:channel
-recipe channel-capacity [
+recipe channel-capacity chan:address:channel -> result:number [
   local-scope
-  chan:address:channel <- next-ingredient
+  load-ingredients
   q:address:array:character <- get *chan, data:offset
-  result:number <- length *q
-  reply result
+  result <- length *q
 ]
 
 scenario channel-new-empty-not-full [
@@ -277,11 +266,9 @@ scenario channel-read-not-full [
 ]
 
 # helper for channels of characters in particular
-# out <- buffer-lines in:address:channel, out:address:channel
-recipe buffer-lines [
+recipe buffer-lines in:address:channel, out:address:channel -> out:address:channel, in:address:channel [
   local-scope
-  in:address:channel <- next-ingredient
-  out:address:channel <- next-ingredient
+  load-ingredients
   # repeat forever
   {
     line:address:buffer <- new-buffer, 30
@@ -327,7 +314,6 @@ recipe buffer-lines [
     }
     loop
   }
-  reply out/same-as-ingredient:1
 ]
 
 scenario buffer-lines-blocks-until-newline [
diff --git a/076stream.mu b/076stream.mu
index 0ee3e2c1..bb441ade 100644
--- a/076stream.mu
+++ b/076stream.mu
@@ -4,41 +4,38 @@ container stream [
   data:address:array:character
 ]
 
-recipe new-stream [
+recipe new-stream s:address:array:character -> result:address:stream [
   local-scope
-  result:address:stream <- new stream:type
+  load-ingredients
+  result <- new stream:type
   i:address:number <- get-address *result, index:offset
   *i <- copy 0
   d:address:address:array:character <- get-address *result, data:offset
-  *d <- next-ingredient
-  reply result
+  *d <- copy s
 ]
 
-recipe rewind-stream [
+recipe rewind-stream in:address:stream -> in:address:stream [
   local-scope
-  in:address:stream <- next-ingredient
+  load-ingredients
   x:address:number <- get-address *in, index:offset
   *x <- copy 0
-  reply in/same-as-arg:0
 ]
 
-recipe read-line [
+recipe read-line in:address:stream -> result:address:array:character, in:address:stream [
   local-scope
-  in:address:stream <- next-ingredient
+  load-ingredients
   idx:address:number <- get-address *in, index:offset
   s:address:array:character <- get *in, data:offset
   next-idx:number <- find-next s, 10/newline, *idx
-  result:address:array:character <- string-copy s, *idx, next-idx
+  result <- string-copy s, *idx, next-idx
   *idx <- add next-idx, 1  # skip newline
-  reply result
 ]
 
-recipe end-of-stream? [
+recipe end-of-stream? in:address:stream -> result:boolean [
   local-scope
-  in:address:stream <- next-ingredient
+  load-ingredients
   idx:number <- get *in, index:offset
   s:address:array:character <- get *in, data:offset
   len:number <- length *s
-  result:boolean <- greater-or-equal idx, len
-  reply result
+  result <- greater-or-equal idx, len
 ]
diff --git a/081print.mu b/081print.mu
index c3d08180..a0c39e9f 100644
--- a/081print.mu
+++ b/081print.mu
@@ -14,13 +14,14 @@ container screen-cell [
   color:number
 ]
 
-recipe new-fake-screen [
+recipe new-fake-screen w:number, h:number -> result:address:screen [
   local-scope
-  result:address:screen <- new screen:type
+  load-ingredients
+  result <- new screen:type
   width:address:number <- get-address *result, num-columns:offset
-  *width <- next-ingredient
+  *width <- copy w
   height:address:number <- get-address *result, num-rows:offset
-  *height <- next-ingredient
+  *height <- copy h
   row:address:number <- get-address *result, cursor-row:offset
   *row <- copy 0
   column:address:number <- get-address *result, cursor-column:offset
@@ -28,18 +29,17 @@ recipe new-fake-screen [
   bufsize:number <- multiply *width, *height
   buf:address:address:array:screen-cell <- get-address *result, data:offset
   *buf <- new screen-cell:type, bufsize
-  clear-screen result
-  reply result
+  result <- clear-screen result
 ]
 
-recipe clear-screen [
+recipe clear-screen screen:address:screen -> screen:address:screen [
   local-scope
-  sc:address:screen <- next-ingredient
+  load-ingredients
   # if x exists
   {
-    break-unless sc
+    break-unless screen
     # clear fake screen
-    buf:address:array:screen-cell <- get *sc, data:offset
+    buf:address:array:screen-cell <- get *screen, data:offset
     max:number <- length *buf
     i:number <- copy 0
     {
@@ -54,32 +54,31 @@ recipe clear-screen [
       loop
     }
     # reset cursor
-    x:address:number <- get-address *sc, cursor-row:offset
+    x:address:number <- get-address *screen, cursor-row:offset
     *x <- copy 0
-    x <- get-address *sc, cursor-column:offset
+    x <- get-address *screen, cursor-column:offset
     *x <- copy 0
-    reply sc/same-as-ingredient:0
+    reply
   }
   # otherwise, real screen
   clear-display
-  reply sc/same-as-ingredient:0
 ]
 
-recipe sync-screen [
+recipe sync-screen screen:address:screen -> screen:address:screen [
   local-scope
-  sc:address:screen <- next-ingredient
+  screen:address:screen <- next-ingredient
   {
-    break-if sc
+    break-if screen
     sync-display
   }
   # do nothing for fake screens
 ]
 
-recipe fake-screen-is-empty? [
+recipe fake-screen-is-empty? screen:address:screen -> result:boolean [
   local-scope
-  sc:address:screen <- next-ingredient
-  reply-unless sc, 1/true
-  buf:address:array:screen-cell <- get *sc, data:offset
+  load-ingredients
+  reply-unless screen, 1/true
+  buf:address:array:screen-cell <- get *screen, data:offset
   i:number <- copy 0
   len:number <- length *buf
   {
@@ -95,10 +94,9 @@ recipe fake-screen-is-empty? [
   reply 1/true
 ]
 
-recipe print-character [
+recipe print-character screen:address:screen, c:character -> screen:address:screen [
   local-scope
-  sc:address:screen <- next-ingredient
-  c:character <- next-ingredient
+  load-ingredients
   color:number, color-found?:boolean <- next-ingredient
   {
     # default color to white
@@ -115,20 +113,20 @@ recipe print-character [
   {
     # if x exists
     # (handle special cases exactly like in the real screen)
-    break-unless sc
-    width:number <- get *sc, num-columns:offset
-    height:number <- get *sc, num-rows:offset
+    break-unless screen
+    width:number <- get *screen, num-columns:offset
+    height:number <- get *screen, num-rows:offset
     # if cursor is out of bounds, silently exit
-    row:address:number <- get-address *sc, cursor-row:offset
+    row:address:number <- get-address *screen, cursor-row:offset
     legal?:boolean <- greater-or-equal *row, 0
-    reply-unless legal?, sc
+    reply-unless legal?
     legal? <- lesser-than *row, height
-    reply-unless legal?, sc
-    column:address:number <- get-address *sc, cursor-column:offset
+    reply-unless legal?
+    column:address:number <- get-address *screen, cursor-column:offset
     legal? <- greater-or-equal *column, 0
-    reply-unless legal?, sc
+    reply-unless legal?
     legal? <- lesser-than *column, width
-    reply-unless legal?, sc
+    reply-unless legal?
     # special-case: newline
     {
       newline?:boolean <- equal c, 10/newline
@@ -142,12 +140,12 @@ recipe print-character [
         *column <- copy 0
         *row <- add *row, 1
       }
-      reply sc/same-as-ingredient:0
+      reply
     }
     # save character in fake screen
     index:number <- multiply *row, width
     index <- add index, *column
-    buf:address:array:screen-cell <- get *sc, data:offset
+    buf:address:array:screen-cell <- get *screen, data:offset
     len:number <- length *buf
     # special-case: backspace
     {
@@ -166,7 +164,7 @@ recipe print-character [
         cursor-color:address:number <- get-address *cursor, color:offset
         *cursor-color <- copy 7/white
       }
-      reply sc/same-as-ingredient:0
+      reply
     }
     cursor:address:screen-cell <- index-address *buf, index
     cursor-contents:address:character <- get-address *cursor, contents:offset
@@ -180,11 +178,10 @@ recipe print-character [
       break-if at-right?
       *column <- add *column, 1
     }
-    reply sc/same-as-ingredient:0
+    reply
   }
   # otherwise, real screen
   print-character-to-display c, color, bg-color
-  reply sc/same-as-ingredient:0
 ]
 
 scenario print-character-at-top-left [
@@ -340,63 +337,58 @@ scenario print-at-bottom-right [
   ]
 ]
 
-recipe clear-line [
+recipe clear-line screen:address:screen -> screen:address:screen [
   local-scope
-  sc:address:screen <- next-ingredient
+  load-ingredients
   # if x exists, clear line in fake screen
   {
-    break-unless sc
-    width:number <- get *sc, num-columns:offset
-    column:address:number <- get-address *sc, cursor-column:offset
+    break-unless screen
+    width:number <- get *screen, num-columns:offset
+    column:address:number <- get-address *screen, cursor-column:offset
     original-column:number <- copy *column
     # space over the entire line
     {
       right:number <- subtract width, 1
       done?:boolean <- greater-or-equal *column, right
       break-if done?
-      print-character sc, [ ]  # implicitly updates 'column'
+      print-character screen, [ ]  # implicitly updates 'column'
       loop
     }
     # now back to where the cursor was
     *column <- copy original-column
-    reply sc/same-as-ingredient:0
+    reply
   }
   # otherwise, real screen
   clear-line-on-display
-  reply sc/same-as-ingredient:0
 ]
 
-recipe cursor-position [
+recipe cursor-position screen:address:screen -> row:number, column:number [
   local-scope
-  sc:address:screen <- next-ingredient
+  load-ingredients
   # if x exists, lookup cursor in fake screen
   {
-    break-unless sc
-    row:number <- get *sc, cursor-row:offset
-    column:number <- get *sc, cursor-column:offset
-    reply row, column, sc/same-as-ingredient:0
+    break-unless screen
+    row:number <- get *screen, cursor-row:offset
+    column:number <- get *screen, cursor-column:offset
+    reply
   }
   row, column <- cursor-position-on-display
-  reply row, column, sc/same-as-ingredient:0
 ]
 
-recipe move-cursor [
+recipe move-cursor screen:address:screen, new-row:number, new-column:number -> screen:address:screen [
   local-scope
-  sc:address:screen <- next-ingredient
-  new-row:number <- next-ingredient
-  new-column:number <- next-ingredient
+  load-ingredients
   # if x exists, move cursor in fake screen
   {
-    break-unless sc
-    row:address:number <- get-address *sc, cursor-row:offset
+    break-unless screen
+    row:address:number <- get-address *screen, cursor-row:offset
     *row <- copy new-row
-    column:address:number <- get-address *sc, cursor-column:offset
+    column:address:number <- get-address *screen, cursor-column:offset
     *column <- copy new-column
-    reply sc/same-as-ingredient:0
+    reply
   }
   # otherwise, real screen
   move-cursor-on-display new-row, new-column
-  reply sc/same-as-ingredient:0
 ]
 
 scenario clear-line-erases-printed-characters [
@@ -429,193 +421,180 @@ scenario clear-line-erases-printed-characters [
   ]
 ]
 
-recipe cursor-down [
+recipe cursor-down screen:address:screen -> screen:address:screen [
   local-scope
-  sc:address:screen <- next-ingredient
+  load-ingredients
   # if x exists, move cursor in fake screen
   {
-    break-unless sc
+    break-unless screen
     {
       # increment row unless it's already all the way down
-      height:number <- get *sc, num-rows:offset
-      row:address:number <- get-address *sc, cursor-row:offset
+      height:number <- get *screen, num-rows:offset
+      row:address:number <- get-address *screen, cursor-row:offset
       max:number <- subtract height, 1
       at-bottom?:boolean <- greater-or-equal *row, max
       break-if at-bottom?
       *row <- add *row, 1
     }
-    reply sc/same-as-ingredient:0
+    reply
   }
   # otherwise, real screen
   move-cursor-down-on-display
-  reply sc/same-as-ingredient:0
 ]
 
-recipe cursor-up [
+recipe cursor-up screen:address:screen -> screen:address:screen [
   local-scope
-  sc:address:screen <- next-ingredient
+  load-ingredients
   # if x exists, move cursor in fake screen
   {
-    break-unless sc
+    break-unless screen
     {
       # decrement row unless it's already all the way up
-      row:address:number <- get-address *sc, cursor-row:offset
+      row:address:number <- get-address *screen, cursor-row:offset
       at-top?:boolean <- lesser-or-equal *row, 0
       break-if at-top?
       *row <- subtract *row, 1
     }
-    reply sc/same-as-ingredient:0
+    reply
   }
   # otherwise, real screen
   move-cursor-up-on-display
-  reply sc/same-as-ingredient:0
 ]
 
-recipe cursor-right [
+recipe cursor-right screen:address:screen -> screen:address:screen [
   local-scope
-  sc:address:screen <- next-ingredient
+  load-ingredients
   # if x exists, move cursor in fake screen
   {
-    break-unless sc
+    break-unless screen
     {
       # increment column unless it's already all the way to the right
-      width:number <- get *sc, num-columns:offset
-      column:address:number <- get-address *sc, cursor-column:offset
+      width:number <- get *screen, num-columns:offset
+      column:address:number <- get-address *screen, cursor-column:offset
       max:number <- subtract width, 1
       at-bottom?:boolean <- greater-or-equal *column, max
       break-if at-bottom?
       *column <- add *column, 1
     }
-    reply sc/same-as-ingredient:0
+    reply
   }
   # otherwise, real screen
   move-cursor-right-on-display
-  reply sc/same-as-ingredient:0
 ]
 
-recipe cursor-left [
+recipe cursor-left screen:address:screen -> screen:address:screen [
   local-scope
-  sc:address:screen <- next-ingredient
+  load-ingredients
   # if x exists, move cursor in fake screen
   {
-    break-unless sc
+    break-unless screen
     {
       # decrement column unless it's already all the way to the left
-      column:address:number <- get-address *sc, cursor-column:offset
+      column:address:number <- get-address *screen, cursor-column:offset
       at-top?:boolean <- lesser-or-equal *column, 0
       break-if at-top?
       *column <- subtract *column, 1
     }
-    reply sc/same-as-ingredient:0
+    reply
   }
   # otherwise, real screen
   move-cursor-left-on-display
-  reply sc/same-as-ingredient:0
 ]
 
-recipe cursor-to-start-of-line [
+recipe cursor-to-start-of-line screen:address:screen -> screen:address:screen [
   local-scope
-  sc:address:screen <- next-ingredient
-  row:number, _, sc <- cursor-position sc
+  load-ingredients
+  row:number <- cursor-position screen
   column:number <- copy 0
-  sc <- move-cursor sc, row, column
-  reply sc/same-as-ingredient:0
+  screen <- move-cursor screen, row, column
 ]
 
-recipe cursor-to-next-line [
+recipe cursor-to-next-line screen:address:screen -> screen:address:screen [
   local-scope
-  screen:address:screen <- next-ingredient
+  load-ingredients
   screen <- cursor-down screen
   screen <- cursor-to-start-of-line screen
-  reply screen/same-as-ingredient:0
 ]
 
-recipe screen-width [
+recipe screen-width screen:address:screen -> width:number [
   local-scope
-  sc:address:screen <- next-ingredient
+  load-ingredients
   # if x exists, move cursor in fake screen
   {
-    break-unless sc
-    width:number <- get *sc, num-columns:offset
-    reply width
+    break-unless screen
+    width <- get *screen, num-columns:offset
+    reply
   }
   # otherwise, real screen
-  width:number <- display-width
-  reply width
+  width <- display-width
 ]
 
-recipe screen-height [
+recipe screen-height screen:address:screen -> height:number [
   local-scope
-  sc:address:screen <- next-ingredient
+  load-ingredients
   # if x exists, move cursor in fake screen
   {
-    break-unless sc
-    height:number <- get *sc, num-rows:offset
-    reply height
+    break-unless screen
+    height <- get *screen, num-rows:offset
+    reply
   }
   # otherwise, real screen
-  height:number <- display-height
-  reply height
+  height <- display-height
 ]
 
-recipe hide-cursor [
+recipe hide-cursor screen:address:screen -> screen:address:screen [
   local-scope
-  screen:address:screen <- next-ingredient
+  load-ingredients
   # if x exists (not real display), do nothing
   {
     break-unless screen
-    reply screen
+    reply
   }
   # otherwise, real screen
   hide-cursor-on-display
-  reply screen
 ]
 
-recipe show-cursor [
+recipe show-cursor screen:address:screen -> screen:address:screen [
   local-scope
-  screen:address:screen <- next-ingredient
+  load-ingredients
   # if x exists (not real display), do nothing
   {
     break-unless screen
-    reply screen
+    reply
   }
   # otherwise, real screen
   show-cursor-on-display
-  reply screen
 ]
 
-recipe hide-screen [
+recipe hide-screen screen:address:screen -> screen:address:screen [
   local-scope
-  screen:address:screen <- next-ingredient
+  load-ingredients
   # if x exists (not real display), do nothing
   # todo: help test this
   {
     break-unless screen
-    reply screen
+    reply
   }
   # otherwise, real screen
   hide-display
-  reply screen
 ]
 
-recipe show-screen [
+recipe show-screen screen:address:screen -> screen:address:screen [
   local-scope
-  screen:address:screen <- next-ingredient
+  load-ingredients
   # if x exists (not real display), do nothing
   # todo: help test this
   {
     break-unless screen
-    reply screen
+    reply
   }
   # otherwise, real screen
   show-display
-  reply screen
 ]
 
-recipe print-string [
+recipe print-string screen:address:screen, s:address:array:character -> screen:address:screen [
   local-scope
-  screen:address:screen <- next-ingredient
-  s:address:array:character <- next-ingredient
+  load-ingredients
   color:number, color-found?:boolean <- next-ingredient
   {
     # default color to white
@@ -638,7 +617,6 @@ recipe print-string [
     i <- add i, 1
     loop
   }
-  reply screen/same-as-ingredient:0
 ]
 
 scenario print-string-stops-at-right-margin [
@@ -661,7 +639,7 @@ scenario print-string-stops-at-right-margin [
   ]
 ]
 
-recipe print-integer [
+recipe print-integer screen:address:screen, n:number -> screen:address:screen [
   local-scope
   screen:address:screen <- next-ingredient
   n:number <- next-ingredient
@@ -679,6 +657,5 @@ recipe print-integer [
   }
   # todo: other bases besides decimal
   s:address:array:character <- integer-to-decimal-string n
-  print-string screen, s, color, bg-color
-  reply screen/same-as-ingredient:0
+  screen <- print-string screen, s, color, bg-color
 ]
diff --git a/084console.mu b/084console.mu
index e713462b..3176b82b 100644
--- a/084console.mu
+++ b/084console.mu
@@ -25,45 +25,45 @@ container console [
   data:address:array:event
 ]
 
-recipe new-fake-console [
+recipe new-fake-console events:address:array:event -> result:address:console [
   local-scope
+  load-ingredients
   result:address:console <- new console:type
   buf:address:address:array:event <- get-address *result, data:offset
-  *buf <- next-ingredient
+  *buf <- copy events
   idx:address:number <- get-address *result, index:offset
   *idx <- copy 0
-  reply result
 ]
 
-recipe read-event [
+recipe read-event console:address:console -> result:event, console:address:console, found?:boolean, quit?:boolean [
   local-scope
-  x:address:console <- next-ingredient
+  load-ingredients
   {
-    break-unless x
-    idx:address:number <- get-address *x, index:offset
-    buf:address:array:event <- get *x, data:offset
+    break-unless console
+    idx:address:number <- get-address *console, index:offset
+    buf:address:array:event <- get *console, data:offset
     {
       max:number <- length *buf
       done?:boolean <- greater-or-equal *idx, max
       break-unless done?
       dummy:address:event <- new event:type
-      reply *dummy, x/same-as-ingredient:0, 1/found, 1/quit
+      reply *dummy, console/same-as-ingredient:0, 1/found, 1/quit
     }
-    result:event <- index *buf, *idx
+    result <- index *buf, *idx
     *idx <- add *idx, 1
-    reply result, x/same-as-ingredient:0, 1/found, 0/quit
+    reply result, console/same-as-ingredient:0, 1/found, 0/quit
   }
   switch  # real event source is infrequent; avoid polling it too much
   result:event, found?:boolean <- check-for-interaction
-  reply result, x/same-as-ingredient:0, found?, 0/quit
+  reply result, console/same-as-ingredient:0, found?, 0/quit
 ]
 
 # variant of read-event for just keyboard events. Discards everything that
 # isn't unicode, so no arrow keys, page-up/page-down, etc. But you still get
 # newlines, tabs, ctrl-d..
-recipe read-key [
+recipe read-key console:address:console -> result:character, console:address:console, found?:boolean, quit?:boolean [
   local-scope
-  console:address:console <- next-ingredient
+  load-ingredients
   x:event, console, found?:boolean, quit?:boolean <- read-event console
   reply-if quit?, 0, console/same-as-ingredient:0, found?, quit?
   reply-unless found?, 0, console/same-as-ingredient:0, found?, quit?
@@ -72,11 +72,9 @@ recipe read-key [
   reply *c, console/same-as-ingredient:0, 1/found, 0/quit
 ]
 
-recipe send-keys-to-channel [
+recipe send-keys-to-channel console:address:console, chan:address:channel, screen:address:screen -> console:address:console, chan:address:channel, screen:address:screen [
   local-scope
-  console:address:console <- next-ingredient
-  chan:address:channel <- next-ingredient
-  screen:address:screen <- next-ingredient
+  load-ingredients
   {
     c:character, console, found?:boolean, quit?:boolean <- read-key console
     loop-unless found?
@@ -86,28 +84,25 @@ recipe send-keys-to-channel [
     chan <- write chan, c
     loop
   }
-  reply console/same-as-ingredient:0, chan/same-as-ingredient:1, screen/same-as-ingredient:2
 ]
 
-recipe wait-for-event [
+recipe wait-for-event console:address:console -> console:address:console [
   local-scope
-  console:address:console <- next-ingredient
+  load-ingredients
   {
     _, console, found?:boolean <- read-event console
     loop-unless found?
   }
-  reply console/same-as-ingredient:0
 ]
 
 # use this helper to skip rendering if there's lots of other events queued up
-recipe has-more-events? [
+recipe has-more-events? console:address:console -> result:boolean [
   local-scope
-  console:address:console <- next-ingredient
+  load-ingredients
   {
     break-unless console
     # fake consoles should be plenty fast; never skip
     reply 0/false
   }
-  result:boolean <- interactions-left?
-  reply result
+  result <- interactions-left?
 ]
diff --git a/channel.mu b/channel.mu
index bc6ee44c..5c3ea735 100644
--- a/channel.mu
+++ b/channel.mu
@@ -1,9 +1,9 @@
 # example program: communicating between routines using channels
 
-recipe producer [
+recipe producer chan:address:channel -> chan:address:channel [
   # produce characters 1 to 5 on a channel
   local-scope
-  chan:address:channel <- next-ingredient
+  load-ingredients
   # n = 0
   n:character <- copy 0
   {
@@ -18,10 +18,10 @@ recipe producer [
   }
 ]
 
-recipe consumer [
+recipe consumer chan:address:channel -> chan:address:channel [
   # consume and print integers from a channel
   local-scope
-  chan:address:channel <- next-ingredient
+  load-ingredients
   {
     # read an integer from the channel
     n:character, chan:address:channel <- read chan
diff --git a/chessboard.mu b/chessboard.mu
index 669e096f..77ce8adb 100644
--- a/chessboard.mu
+++ b/chessboard.mu
@@ -17,7 +17,7 @@ recipe main [
   #
   # Here the console and screen are both 0, which usually indicates real
   # hardware rather than a fake for testing as you'll see below.
-  0/screen, 0/console <- chessboard 0/screen, 0/console
+  chessboard 0/screen, 0/console
 
   close-console  # cleanup screen, keyboard and mouse
 ]
@@ -66,10 +66,9 @@ scenario print-board-and-read-move [
 
 ## Here's how 'chessboard' is implemented.
 
-recipe chessboard [
+recipe chessboard screen:address:screen, console:address:console -> screen:address:screen, console:address:console [
   local-scope
-  screen:address:screen <- next-ingredient
-  console:address:console <- next-ingredient
+  load-ingredients
   board:address:array:address:array:character <- initial-position
   # hook up stdin
   stdin:address:channel <- new-channel 10/capacity
@@ -94,14 +93,14 @@ recipe chessboard [
     {
       cursor-to-next-line screen
       msg <- new [move: ]
-      print-string screen, msg
+      screen <- print-string screen, msg
       m:address:move, quit:boolean, error:boolean <- read-move buffered-stdin, screen
       break-if quit, +quit:label
       buffered-stdin <- clear-channel buffered-stdin  # cleanup after error. todo: test this?
       loop-if error
     }
     board <- make-move board, m
-    clear-screen screen
+    screen <- clear-screen screen
     loop
   }
   +quit
@@ -109,15 +108,15 @@ recipe chessboard [
 
 ## a board is an array of files, a file is an array of characters (squares)
 
-recipe new-board [
+recipe new-board initial-position:address:array:character -> board:address:array:address:array:character [
   local-scope
-  initial-position:address:array:character <- next-ingredient
+  load-ingredients
   # assert(length(initial-position) == 64)
   len:number <- length *initial-position
   correct-length?:boolean <- equal len, 64
   assert correct-length?, [chessboard had incorrect size]
   # board is an array of pointers to files; file is an array of characters
-  board:address:array:address:array:character <- new location:type, 8
+  board <- new location:type, 8
   col:number <- copy 0
   {
     done?:boolean <- equal col, 8
@@ -127,15 +126,13 @@ recipe new-board [
     col <- add col, 1
     loop
   }
-  reply board
 ]
 
-recipe new-file [
+recipe new-file position:address:array:character, index:number -> result:address:array:character [
   local-scope
-  position:address:array:character <- next-ingredient
-  index:number <- next-ingredient
+  load-ingredients
   index <- multiply index, 8
-  result:address:array:character <- new character:type, 8
+  result <- new character:type, 8
   row:number <- copy 0
   {
     done?:boolean <- equal row, 8
@@ -146,13 +143,11 @@ recipe new-file [
     index <- add index, 1
     loop
   }
-  reply result
 ]
 
-recipe print-board [
+recipe print-board screen:address:screen, board:address:array:address:array:character -> screen:address:screen [
   local-scope
-  screen:address:screen <- next-ingredient
-  board:address:array:address:array:character <- next-ingredient
+  load-ingredients
   row:number <- copy 7  # start printing from the top of the board
   # print each row
   {
@@ -188,8 +183,7 @@ recipe print-board [
   screen <- cursor-to-next-line screen
 ]
 
-# board:address:array:address:array:character <- initial-position
-recipe initial-position [
+recipe initial-position -> board:address:array:address:array:character [
   local-scope
   # layout in memory (in raster order):
   #   R P _ _ _ _ p r
@@ -209,8 +203,7 @@ recipe initial-position [
 #?       66/B, 80/P, 32/blank, 32/blank, 32/blank, 32/blank, 112/p, 98/b,
 #?       78/N, 80/P, 32/blank, 32/blank, 32/blank, 32/blank, 112/p, 110/n,
 #?       82/R, 80/P, 32/blank, 32/blank, 32/blank, 32/blank, 112/p, 114/r
-  board:address:array:address:array:character <- new-board initial-position
-  reply board
+  board <- new-board initial-position
 ]
 
 scenario printing-the-board [
@@ -246,12 +239,10 @@ container move [
   to-rank:number
 ]
 
-# result:address:move, quit?:boolean, error?:boolean <- read-move stdin:address:channel, screen:address:screen
 # prints only error messages to screen
-recipe read-move [
+recipe read-move stdin:address:channel, screen:address:screen -> result:address:move, quit?:boolean, error?:boolean [
   local-scope
-  stdin:address:channel <- next-ingredient
-  screen:address:screen <- next-ingredient
+  load-ingredients
   from-file:number, quit?:boolean, error?:boolean <- read-file stdin, screen
   reply-if quit?, 0/dummy, quit?, error?
   reply-if error?, 0/dummy, quit?, error?
@@ -278,12 +269,10 @@ recipe read-move [
   reply result, quit?, error?
 ]
 
-# file:number, quit:boolean, error:boolean <- read-file stdin:address:channel, screen:address:screen
 # valid values for file: 0-7
-recipe read-file [
+recipe read-file stdin:address:channel, screen:address:screen -> file:number, quit:boolean, error:boolean [
   local-scope
-  stdin:address:channel <- next-ingredient
-  screen:address:screen <- next-ingredient
+  load-ingredients
   c:character, stdin <- read stdin
   {
     q-pressed?:boolean <- equal c, 81/Q
@@ -329,12 +318,10 @@ recipe read-file [
   reply file, 0/quit, 0/error
 ]
 
-# rank:number <- read-rank stdin:address:channel, screen:address:screen
 # valid values: 0-7, -1 (quit), -2 (error)
-recipe read-rank [
+recipe read-rank stdin:address:channel, screen:address:screen -> rank:number, quit?:boolean, error?:boolean, stdin:address:channel, screen:address:screen [
   local-scope
-  stdin:address:channel <- next-ingredient
-  screen:address:screen <- next-ingredient
+  load-ingredients
   c:character, stdin <- read stdin
   {
     q-pressed?:boolean <- equal c, 8/Q
@@ -376,11 +363,9 @@ recipe read-rank [
 
 # read a character from the given channel and check that it's what we expect
 # return true on error
-recipe expect-from-channel [
+recipe expect-from-channel stdin:address:channel, expected:character, screen:address:screen -> result:boolean [
   local-scope
-  stdin:address:channel <- next-ingredient
-  expected:character <- next-ingredient
-  screen:address:screen <- next-ingredient
+  load-ingredients
   c:character, stdin <- read stdin
   {
     match?:boolean <- equal c, expected
@@ -388,8 +373,7 @@ recipe expect-from-channel [
     s:address:array:character <- new [expected character not found]
     print-string screen, s
   }
-  result:boolean <- not match?
-  reply result
+  result <- not match?
 ]
 
 scenario read-move-blocking [
@@ -556,21 +540,19 @@ F read-move-file: routine failed to pause after coming up (before any keys were
   ]
 ]
 
-recipe make-move [
+recipe make-move board:address:array:address:array:character, m:address:move -> board:address:array:address:array:character [
   local-scope
-  b:address:array:address:array:character <- next-ingredient
-  m:address:move <- next-ingredient
+  load-ingredients
   from-file:number <- get *m, from-file:offset
   from-rank:number <- get *m, from-rank:offset
   to-file:number <- get *m, to-file:offset
   to-rank:number <- get *m, to-rank:offset
-  f:address:array:character <- index *b, from-file
+  f:address:array:character <- index *board, from-file
   src:address:character/square <- index-address *f, from-rank
-  f <- index *b, to-file
+  f <- index *board, to-file
   dest:address:character/square <- index-address *f, to-rank
   *dest <- copy *src
   *src <- copy 32/space
-  reply b/same-as-ingredient:0
 ]
 
 scenario making-a-move [
diff --git a/edit/001-editor.mu b/edit/001-editor.mu
index c74e6ac4..ae9e5219 100644
--- a/edit/001-editor.mu
+++ b/edit/001-editor.mu
@@ -47,7 +47,7 @@ container editor-data [
 # creates a new editor widget and renders its initial appearance to screen
 #   top/left/right constrain the screen area available to the new editor
 #   right is exclusive
-recipe new-editor s:address:array:character, screen:address:screen, left:number, right:number -> result:address:editor-data [
+recipe new-editor s:address:array:character, screen:address:screen, left:number, right:number -> result:address:editor-data, screen:address:screen [
   local-scope
   load-ingredients
   # no clipping of bounds
@@ -224,13 +224,13 @@ recipe render screen:address:screen, editor:address:editor-data -> last-row:numb
   reply row, column, screen/same-as-ingredient:0, editor/same-as-ingredient:1
 ]
 
-recipe clear-line-delimited screen:address:screen, column:number, right:number [
+recipe clear-line-delimited screen:address:screen, column:number, right:number -> screen:address:screen [
   local-scope
   load-ingredients
   {
     done?:boolean <- greater-than column, right
     break-if done?
-    print-character screen, 32/space
+    screen <- print-character screen, 32/space
     column <- add column, 1
     loop
   }