diff --git a/examples/fn-root-vars.ion b/examples/fn-root-vars.ion
new file mode 100644
index 0000000000000000000000000000000000000000..ee7dc005bb9538db58f0c4a78e6e2eb9a7d50e31
--- /dev/null
+++ b/examples/fn-root-vars.ion
@@ -0,0 +1,9 @@
+fn env
+  echo $HOME
+  echo $PROMPT
+  echo $UID
+  echo $CDPATH
+end
+
+env
+
diff --git a/examples/fn-root-vars.out b/examples/fn-root-vars.out
new file mode 100644
index 0000000000000000000000000000000000000000..bae0b7bbc27cb2df6fdab3c851feef354b35e99f
--- /dev/null
+++ b/examples/fn-root-vars.out
@@ -0,0 +1,4 @@
+/home/adminxvii
+${x::1B}]0;${USER}: ${PWD}${x::07}${c::0x55,bold}${USER}${c::default}:${c::0x4B}${SWD}${c::default}# ${c::reset}
+1000
+
diff --git a/examples/glob.out b/examples/glob.out
index 5f5f7ec03fb2ddc7d404c2397a5e59649e900cc7..209f4f579dae55ce46f9d8147bdee1ce073547d7 100644
--- a/examples/glob.out
+++ b/examples/glob.out
@@ -5,7 +5,7 @@ Cargo.toml
 Cargo.lock Cargo.toml
 Cargo.toml
 Cargo.toml
-examples/else_if.ion examples/empty_loop_test.ion examples/exists.ion examples/fail.ion examples/fibonacci.ion examples/fn.ion examples/for.ion examples/function_piping.ion
+examples/else_if.ion examples/empty_loop_test.ion examples/exists.ion examples/fail.ion examples/fibonacci.ion examples/fn-root-vars.ion examples/fn.ion examples/for.ion examples/function_piping.ion
 []
 [] []
 one three two
diff --git a/examples/run_examples.sh b/examples/run_examples.sh
index 627cb60c7ceed2f6196fb51cfc7c6f0f356b7f02..c7ca529393082af7a11cd45a0f7f383d23aa43a7 100755
--- a/examples/run_examples.sh
+++ b/examples/run_examples.sh
@@ -21,6 +21,12 @@ EXIT_VAL=0
 # and it never hurts to force consistency regardless
 cd $PROJECT_DIR
 
+# Create expected output for fn-root-vars
+echo $HOME > examples/fn-root-vars.out # Overwrite previous file
+echo '${x::1B}]0;${USER}: ${PWD}${x::07}${c::0x55,bold}${USER}${c::default}:${c::0x4B}${SWD}${c::default}# ${c::reset}' >> examples/fn-root-vars.out
+echo $UID >> examples/fn-root-vars.out
+echo >> examples/fn-root-vars.out
+
 function test {
     # Replace .ion with .out in file name
     EXPECTED_OUTPUT_FILE=$(echo $1 | sed 's/\..\+/\.out/')
diff --git a/examples/scopes.ion b/examples/scopes.ion
index dcb6a4eabd072f13b258873e301a69eee486c784..cfc53af6c00632de6cb82e4257e5a9f731b557da 100644
--- a/examples/scopes.ion
+++ b/examples/scopes.ion
@@ -26,3 +26,16 @@ end
 
 echo $x
 echo $y
+fn demo
+  echo ${super::foo}
+  drop foo
+
+  fn bar
+    super::demo
+  end
+  bar
+end
+
+let foo = bar
+echo $foo
+demo
diff --git a/examples/scopes.out b/examples/scopes.out
index 15020d142b9d1f487c1adbd7382b5d219879b334..cc6de1ce253ed648155b426c60212953817662d9 100644
--- a/examples/scopes.out
+++ b/examples/scopes.out
@@ -4,3 +4,7 @@
 
 
 
+bar
+bar
+ion: undefined variable: foo
+ion: command not found: super::demo
diff --git a/src/lib/shell/binary/prompt.rs b/src/lib/shell/binary/prompt.rs
index ea05dcf75322a0bd5fe205c8d37b2d7040c4bc92..0ce6a6a3a366e7ad1d8bb13241e156a7c415dc3c 100644
--- a/src/lib/shell/binary/prompt.rs
+++ b/src/lib/shell/binary/prompt.rs
@@ -1,6 +1,6 @@
 use crate::{
     parser::shell_expand::expand_string,
-    shell::{flags::UNTERMINATED, Capture, Function, Shell},
+    shell::{flags::UNTERMINATED, variables::Value, Capture, Shell},
     sys,
 };
 use std::{io::Read, process};
@@ -18,29 +18,30 @@ pub(crate) fn prompt(shell: &mut Shell) -> String {
 }
 
 pub(crate) fn prompt_fn(shell: &mut Shell) -> Option<String> {
-    let function = shell.variables.get::<Function>("PROMPT")?;
-    let function = &function as *const Function;
-
-    let mut output = None;
-
-    match shell.fork(Capture::StdoutThenIgnoreStderr, |child| {
-        let _ = unsafe { function.read() }.execute(child, &["ion"]);
-    }) {
-        Ok(result) => {
-            let mut string = String::with_capacity(1024);
-            match result.stdout.unwrap().read_to_string(&mut string) {
-                Ok(_) => output = Some(string),
-                Err(why) => {
-                    eprintln!("ion: error reading stdout of child: {}", why);
+    if let Some(Value::Function(function)) = shell.variables.get_ref("PROMPT") {
+        let output = match shell.fork(Capture::StdoutThenIgnoreStderr, |child| {
+            let _ = function.execute(child, &["ion"]);
+        }) {
+            Ok(result) => {
+                let mut string = String::with_capacity(1024);
+                match result.stdout?.read_to_string(&mut string) {
+                    Ok(_) => Some(string),
+                    Err(why) => {
+                        eprintln!("ion: error reading stdout of child: {}", why);
+                        None
+                    }
                 }
             }
-        }
-        Err(why) => {
-            eprintln!("ion: fork error: {}", why);
-        }
-    }
+            Err(why) => {
+                eprintln!("ion: fork error: {}", why);
+                None
+            }
+        };
 
-    // Ensure that the parent retains ownership of the terminal before exiting.
-    let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id());
-    output
+        // Ensure that the parent retains ownership of the terminal before exiting.
+        let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id());
+        output
+    } else {
+        None
+    }
 }
diff --git a/src/lib/shell/flow.rs b/src/lib/shell/flow.rs
index 962944302b6e2dbf3d3d6e12b6c1c517f4d0f508..eaed0f3b19fcb64a46cd91ceb41d7a3c36cf9200 100644
--- a/src/lib/shell/flow.rs
+++ b/src/lib/shell/flow.rs
@@ -399,7 +399,7 @@ fn expand_pipeline(
     shell: &Shell,
     pipeline: &Pipeline,
 ) -> Result<(Pipeline, Vec<Statement>), String> {
-    let mut item_iter = pipeline.items.iter().cloned();
+    let mut item_iter = pipeline.items.iter();
     let mut items: Vec<PipeItem> = Vec::with_capacity(item_iter.size_hint().0);
     let mut statements = Vec::new();
 
@@ -443,7 +443,7 @@ fn expand_pipeline(
                             }
                             // Append rest of the pipeline to the last pipeline in the
                             // alias.
-                            pline.items.extend(item_iter);
+                            pline.items.extend(item_iter.cloned());
                         } else {
                             // Error in expansion
                             return Err(format!(
diff --git a/src/lib/shell/flow_control.rs b/src/lib/shell/flow_control.rs
index a1dc7d771e946966ec0256689a59d3c416dbff4d..bd744e97fdd652ffd8658ef9a8308a78c4948e7b 100644
--- a/src/lib/shell/flow_control.rs
+++ b/src/lib/shell/flow_control.rs
@@ -378,7 +378,7 @@ impl Function {
     pub fn is_empty(&self) -> bool { self.statements.is_empty() }
 
     pub(crate) fn execute<S: AsRef<str>>(
-        self,
+        &self,
         shell: &mut Shell,
         args: &[S],
     ) -> Result<(), FunctionError> {
diff --git a/src/lib/shell/fork_function.rs b/src/lib/shell/fork_function.rs
index cac2488b75e52ae8debcac2a87cf47b40544eee9..62066ba9d2c1fd0e2367508cffb3fea8e0be96bd 100644
--- a/src/lib/shell/fork_function.rs
+++ b/src/lib/shell/fork_function.rs
@@ -1,5 +1,5 @@
 use crate::{
-    shell::{Capture, Function, Shell},
+    shell::{variables::Value, Capture, Shell},
     sys,
 };
 use std::process;
@@ -11,23 +11,20 @@ pub(crate) fn command_not_found(shell: &mut Shell, command: &str) -> bool {
 /// High-level function for executing a function programmatically.
 /// NOTE: Always add "ion" as a first argument in `args`.
 pub fn fork_function<S: AsRef<str>>(shell: &mut Shell, fn_name: &str, args: &[S]) -> bool {
-    let function: Function = match shell.variables.get::<Function>(fn_name) {
-        Some(func) => func,
-        None => return false,
-    };
-    let function = &function as *const Function;
-
-    if let Err(err) = shell.fork(Capture::None, |child| {
-        let result = unsafe { function.read() }.execute(child, args);
-        if let Err(err) = result {
-            eprintln!("ion: {} function call: {}", fn_name, err);
+    if let Some(Value::Function(function)) = shell.variables.get_ref(fn_name) {
+        if let Err(err) = shell.fork(Capture::None, |child| {
+            if let Err(err) = function.execute(child, args) {
+                eprintln!("ion: {} function call: {}", fn_name, err);
+            }
+        }) {
+            eprintln!("ion: fork error: {}", err);
+            false
+        } else {
+            // Ensure that the parent retains ownership of the terminal before exiting.
+            let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id());
+            true
         }
-    }) {
-        eprintln!("ion: fork error: {}", err);
-        return false;
+    } else {
+        false
     }
-
-    // Ensure that the parent retains ownership of the terminal before exiting.
-    let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id());
-    true
 }
diff --git a/src/lib/shell/variables/mod.rs b/src/lib/shell/variables/mod.rs
index b4714cde85cc85760cf1047193cde684fb59c86e..0809082f52ee183b7be5dac3ef411a5bfce6d15a 100644
--- a/src/lib/shell/variables/mod.rs
+++ b/src/lib/shell/variables/mod.rs
@@ -273,7 +273,7 @@ impl Variables {
         self.scopes.extend(scopes);
     }
 
-    pub fn scopes(&self) -> impl Iterator<Item = &Scope> {
+    pub fn scopes(&self) -> impl DoubleEndedIterator<Item = &Scope> {
         let amount = self.scopes.len() - self.current - 1;
         self.scopes.iter().rev().skip(amount)
     }
@@ -301,33 +301,38 @@ impl Variables {
         const GLOBAL_NS: &str = "global::";
         const SUPER_NS: &str = "super::";
 
-        let mut up_namespace: isize = if name.starts_with(GLOBAL_NS) {
+        if name.starts_with(GLOBAL_NS) {
             name = &name[GLOBAL_NS.len()..];
             // Go up as many namespaces as possible
-            self.scopes().filter(|scope| scope.namespace).count() as isize
-        } else {
+            self.scopes()
+                .rev()
+                .take_while(|scope| !scope.namespace)
+                .filter_map(|scope| scope.get(name))
+                .last()
+        } else if name.starts_with(SUPER_NS) {
             let mut up = 0;
             while name.starts_with(SUPER_NS) {
                 name = &name[SUPER_NS.len()..];
                 up += 1;
             }
 
-            up
-        };
-
-        for scope in self.scopes() {
-            match scope.get(name) {
-                val @ Some(Value::Function(_)) => return val,
-                val @ Some(_) if up_namespace == 0 => return val,
-                _ => (),
+            for scope in self.scopes() {
+                if up == 0 {
+                    let val = scope.get(name);
+                    if val.is_some() {
+                        return val;
+                    } else if scope.namespace {
+                        break;
+                    }
+                } else if scope.namespace {
+                    up -= 1;
+                }
             }
 
-            if scope.namespace {
-                up_namespace -= 1;
-            }
+            None
+        } else {
+            self.scopes().filter_map(|scope| scope.get(name)).next()
         }
-
-        None
     }
 
     pub fn get_mut(&mut self, name: &str) -> Option<&mut Value> {
@@ -348,10 +353,18 @@ impl Variables {
     }
 
     pub fn remove_variable(&mut self, name: &str) -> Option<Value> {
+        if name.starts_with("super::") || name.starts_with("global::") {
+            // Cannot mutate outer namespace
+            return None;
+        }
         for scope in self.scopes_mut() {
+            let exit = scope.namespace;
             if let val @ Some(_) = scope.remove(name) {
                 return val;
             }
+            if exit {
+                break;
+            }
         }
         None
     }