 compileFnImpl = (body, wheres, doReturn) ->
   if not body
     throw new Error "Missing definition of a function"
+  # Find invalid (hoistable) wheres and let the function body
+  # define them
+  [validWheres, hoistableWheres] = findHoistableWheres wheres
+  wheresDef = compileWhereImpl validWheres
+  bodyDef = compileImpl body, hoistableWheres
   [
-    compileWhereImpl wheres
+    wheresDef
   ,
     if doReturn
-      "return #{compileImpl body};"
-    else
-      compileImpl body
+      "return #{bodyDef};"
+    else
+      bodyDef
   ].join '\n'
+
+# Returns two lists, first the wheres that can be defined, second
+# the wheres which use an identifier which is not in scope (possibly
+# defined inside match)
+
+  # get list of names from wheres
+  # for each where, get list of used names
+  #   if some used name is not in newly defined or any parent scope
+  #     save it for later, maybe it's defined inside body
+findHoistableWheres = (wheres) ->
+  names = {}
+  hoistable = []
+  valid = []
+  for [pattern, def], i in wheres
+    crawl pattern, (node) ->
+      if node.label is 'name'
+        names[node.token] = yes
+  for [pattern, def], i in wheres
+    log "def", printAst def
+    decided = no
+    crawl def, (node) ->
+      if not node.label
+        name = node.token
+        if not names[name] and not lookupIdentifier name, node
+          if not decided
+            hoistable.push [pattern, def]
+            decided = yes
+          # also save all undefined names
+          (hoistable[hoistable.length - 1][2] ?= []).push name
+    if not decided
+      valid.push [pattern, def]
+  [valid, hoistable]
+
 
 compileWhereImpl = (wheres) ->
   sorted = (sortTopologically constructDependencyGraph wheres).map ({def}) -> def

  else
    compileDef [name, def]

compileImpl = (node) ->
  switch node.type
    when 'comment' then 'null'
    when 'data' then 'null'
    when 'type' then 'null'
    when 'function' then compileFn node
    else
      if Array.isArray node
        exps = inside node

# exp followed by list of definitions
compiled = (source) ->
  variableCounter = 1
  compileTop preCompileTop source

# single exp
compiledExp = (source) ->
  variableCounter = 1
  "(#{compileImpl preCompileExp source})"

# list of definitions
compileDefinitions = (source) ->
  variableCounter = 1
  compileWheres preCompileDefs source

# exported list of definitions
compileDefinitionsInModule = (source) ->
  variableCounter = 1
  compileWheresInModule preCompileDefs source

preCompileExp = (source) ->
  parentize typify astize tokenize source

preCompileTop = (source) ->
  parentize typifyTop astize tokenize "(#{source})"

preCompileDefs = (source) ->
  parentize typifyDefinitions astize tokenize "(#{source})"

compileTop = (ast) ->
  ast.scope = {}
