   precs: precs ? []
   assigns: assigns ? []
 
-macroCompile = (ctx, call) ->
-  op = _operator call
-  op.label = 'keyword'
-  expanded = ctx.macros()[op.token] ctx, call
-  if not isWellformed expanded
-    'malformed'
-  else if isTranslated expanded
-    expanded
-  else
-    expressionCompile ctx, expanded
-
-isTranslated = (result) ->
-  typeof result is 'string' or result instanceof String
-
 topLevel = (ctx, form) ->
-  listOfLines (for [name, def] in pairs _terms form
-    # for now just atom names
-    # syntaxNewName 'Name pattern required', name
-    ctx.setName name
-    compiled = expressionCompile ctx, def
-    ctx.resetName()
-    compiled)
+  compiledPairs = []
+  for [lhs, rhs] in pairs _terms form
+    topLevelPairCompile ctx, compiledPairs, lhs, rhs
+
+  if ctx.deferred()
+    log ctx.deferred()
+    deferredCount = 0
+    while (_notEmpty ctx.deferred()) and deferredCount < ctx.deferred().length
+      [dependencyName, lhs, rhs] = deferred = ctx.deferred().shift()
+      if ctx.declaration dependencyName
+        deferredCount = 0
+        topLevelPairCompile ctx, compiledPairs, lhs, rhs
+      else
+        # If can't compile, defer further
+        deferredCount++
+        ctx.addDeferred deferred
+
+  listOfLines compiledPairs
+
+topLevelPairCompile = (ctx, compiledPairs, lhs, rhs) ->
+  ctx.setName lhs
+  compiled = expressionCompile ctx, rhs
+  ctx.resetName()
+  if ctx.shouldDefer()
+    ctx.setDefer false
+  else
+    compiledPairs.push compiled
 
 builtInMacros =
 

      compiledBody = termCompile ctx, body
      # log "compiled", body.tea
      ctx.closeScope()
      call.tea = typeFn paramTypes..., body.tea

      # Syntax
      isUsedParam = (expression) ->
        (isName expression) and (_token expression) in paramNames
      map (syntaxNameAs '', 'param'), filterAst isUsedParam, body

      # Arity
      if ctx._name()
        ctx.addArity ctx._name(), paramNames

      assignCompile ctx, call,
        """λ(function (#{listOf paramNames}) {

      return malformed 'Matching on sequences requires at least one element name', form

    compiledArgs = (for elem, i in elems
      [lhs, rhs] =
        if isSplat elem
          elem.label = 'name'
          [(splatToName elem), "seq_splat(#{i}, #{elems.length - i - 1}, #{sequence})"]
        else
          [elem, "seq_at(#{i}, #{sequence})"]
      ctx.setAssignTo rhs
      lhsCompiled = expressionCompile ctx, lhs
      retrieve elem, lhs
      ctx.resetAssignTo()
      lhsCompiled)
    elemType = ctx.freshTypeVariable star
    for elem in elems
      unify ctx, elemType, elem.tea
    # TODO use (Seq c e) instead of (Array e)
    form.tea = new TypeApp arrayType, elemType
    cond = "seq_size(#{sequence}) #{if hasSplat then '>=' else '=='} #{requiredElems}"
    combinePatterns join [(precs: [(cond_ cond)])], compiledArgs
  else
    # The below worked, but not for patterns
    # Compile as calls for type checking
    # {a b c} to (& a (& b (& c)))
    # expressionCompile ctx, arrayToConses elems
    # result =>         "[#{listOf map _compiled, elems}]"

    elemType = ctx.freshTypeVariable star
    compiledElems = termsCompile ctx, elems
    for elem in elems
      unify ctx, elemType, elem.tea

    form.label = 'operator'
    form.tea = new TypeApp arrayType, elemType
    assignCompile ctx, form, "[#{listOf compiledElems}]"

isSplat = (expression) ->
  (isAtom expression) and (_token expression)[...2] is '..'

splatToName = (splat) ->
  replicate splat,
    (token_ (_token splat)[2...])

# arrayToConses = (elems) ->
#   if elems.length is 0
#     token_ 'empty-array'
#   else
#     [x, xs...] = elems
#     (call_ (token_ 'cons-array'), [x, (arrayToConses xs)])

assignCompile = (ctx, expression, translatedExpression) ->
  if to = ctx._nameExpression()

    ctx.setGroupTranslation()
    {precs, assigns} = patternCompile ctx, to, expression, translatedExpression

    if ctx.shouldDefer()
      ctx.addDeferred ctx.shouldDefer(), to, expression
      return 'deferred'

    if assigns.length is 0
      throw new "No assign in assignCompile"
