diff --git a/src/binary/prompt.rs b/src/binary/prompt.rs
index ed51d259bd1eae8d9e3d76381c55cde0a6805145..0212a566653ae6898e665a0f0562d2f317d1c416 100644
--- a/src/binary/prompt.rs
+++ b/src/binary/prompt.rs
@@ -1,5 +1,5 @@
-use ion_shell::{parser::Expander, sys, Capture, Shell, Value};
-use std::{io::Read, process};
+use ion_shell::{parser::Expander, Capture, Shell};
+use std::io::Read;
 
 pub fn prompt(shell: &Shell) -> String {
     let blocks = shell.block_len() + if shell.unterminated { 1 } else { 0 };
@@ -14,30 +14,21 @@ pub fn prompt(shell: &Shell) -> String {
 }
 
 pub fn prompt_fn(shell: &Shell) -> Option<String> {
-    if let Some(Value::Function(function)) = shell.variables().get_ref("PROMPT") {
-        let output = match shell.fork(Capture::StdoutThenIgnoreStderr, move |child| {
-            let _ = function.execute(child, &["ion"]);
-        }) {
-            Ok(result) => {
+    shell
+        .fork_function(
+            Capture::StdoutThenIgnoreStderr,
+            |result| {
                 let mut string = String::with_capacity(1024);
-                match result.stdout?.read_to_string(&mut string) {
-                    Ok(_) => Some(string),
+                match result.stdout.ok_or(())?.read_to_string(&mut string) {
+                    Ok(_) => Ok(string),
                     Err(why) => {
                         eprintln!("ion: error reading stdout of child: {}", why);
-                        None
+                        Err(())
                     }
                 }
-            }
-            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
-    } else {
-        None
-    }
+            },
+            "PROMPT",
+            &["ion"],
+        )
+        .ok()
 }
diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs
index 57c644debb2826c243cb3ae9c7862b0a7c789a37..e94042ea8948677b303530dbee9af223aa1ecf20 100644
--- a/src/lib/builtins/mod.rs
+++ b/src/lib/builtins/mod.rs
@@ -38,7 +38,7 @@ use hashbrown::HashMap;
 use liner::{Completer, Context};
 
 use crate::{
-    shell::{self, status::*, ProcessState, Shell},
+    shell::{self, status::*, Capture, ProcessState, Shell},
     sys, types,
 };
 use itertools::Itertools;
@@ -270,7 +270,7 @@ pub fn builtin_cd(args: &[small::String], shell: &mut Shell) -> i32 {
 
     match shell.cd(args.get(1)) {
         Ok(()) => {
-            let _ = shell.fork_function("CD_CHANGE", &["ion"]);
+            let _ = shell.fork_function(Capture::None, |_| Ok(()), "CD_CHANGE", &["ion"]);
             SUCCESS
         }
         Err(why) => {
diff --git a/src/lib/lib.rs b/src/lib/lib.rs
index 5ff9351307e2db060740a28e13b98e88bfdcc599..3063d8491649a8089e3193175914be85a67d46df 100644
--- a/src/lib/lib.rs
+++ b/src/lib/lib.rs
@@ -5,7 +5,7 @@ extern crate err_derive;
 extern crate ion_braces as braces;
 extern crate ion_lexers as lexers;
 extern crate ion_ranges as ranges;
-pub extern crate ion_sys as sys;
+extern crate ion_sys as sys;
 
 #[macro_use]
 pub mod types;
diff --git a/src/lib/shell/fork.rs b/src/lib/shell/fork.rs
index d315ff642f16cbdf4c8f93850cd6f687282bf218..513edfcc7de6eacba93c385504ea795b6a450c2e 100644
--- a/src/lib/shell/fork.rs
+++ b/src/lib/shell/fork.rs
@@ -20,7 +20,7 @@ pub fn wait_for_child(pid: u32) -> io::Result<u8> {
 }
 
 #[repr(u8)]
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
 /// Instructs whether or not to capture the standard output and error streams.
 ///
 /// A type that is utilized by the `Fork` structure.
diff --git a/src/lib/shell/fork_function.rs b/src/lib/shell/fork_function.rs
index d20bbdec9bd486f24f7e68fb3f33f5556319b217..8e872dde5db601b68e678668df19e38ea6c428fc 100644
--- a/src/lib/shell/fork_function.rs
+++ b/src/lib/shell/fork_function.rs
@@ -1,5 +1,5 @@
 use crate::{
-    shell::{variables::Value, Capture, Shell},
+    shell::{fork::IonResult, variables::Value, Capture, Shell},
     sys,
 };
 use std::process;
@@ -7,22 +7,40 @@ use std::process;
 impl<'a> Shell<'a> {
     /// 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>>(&mut self, fn_name: &str, args: &[S]) -> Result<(), ()> {
+    pub fn fork_function<S: AsRef<str>, T, F: FnOnce(IonResult) -> Result<T, ()>>(
+        &self,
+        capture: Capture,
+        result: F,
+        fn_name: &str,
+        args: &[S],
+    ) -> Result<T, ()> {
         if let Some(Value::Function(function)) = self.variables.get_ref(fn_name) {
-            if let Err(err) = self.fork(Capture::None, move |child| {
-                if let Err(err) = function.execute(child, args) {
-                    eprintln!("ion: {} function call: {}", fn_name, err);
-                }
-            }) {
-                eprintln!("ion: fork error: {}", err);
-                Err(())
-            } else {
-                // Ensure that the parent retains ownership of the terminal before exiting.
-                let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id());
-                Ok(())
-            }
+            let output = self
+                .fork(capture, move |child| {
+                    if let Err(err) = function.execute(child, args) {
+                        if capture == Capture::None {
+                            eprintln!("ion: {} function call: {}", fn_name, err);
+                        }
+                    }
+                })
+                .map_err(|err| eprintln!("ion: fork error: {}", err))
+                .and_then(result);
+
+            // Ensure that the parent retains ownership of the terminal before exiting.
+            let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id());
+            output
         } else {
             Err(())
         }
     }
+
+    /// Execute the function on command not found
+    pub fn command_not_found<S: AsRef<str>>(&self, cmd: S) {
+        if self
+            .fork_function(Capture::None, |_| Ok(()), "COMMAND_NOT_FOUND", &["ion", cmd.as_ref()])
+            .is_err()
+        {
+            eprintln!("ion: command not found: {}", cmd.as_ref());
+        }
+    }
 }
diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs
index 373de69af8b212c4dcd949c22beb50e19a49e271..e97779c97a40c5a715be697b09f834b0a3cede95 100644
--- a/src/lib/shell/mod.rs
+++ b/src/lib/shell/mod.rs
@@ -12,8 +12,7 @@ pub(crate) mod signals;
 pub mod status;
 pub mod variables;
 
-pub use self::{fork::Capture, job::Job, pipe_exec::job_control::ProcessState, variables::Value};
-
+pub(crate) use self::job::Job;
 use self::{
     directory_stack::DirectoryStack,
     flow_control::{Block, Function, FunctionError, Statement},
@@ -23,6 +22,7 @@ use self::{
     status::*,
     variables::{GetVariable, Variables},
 };
+pub use self::{fork::Capture, pipe_exec::job_control::ProcessState, variables::Value};
 use crate::{
     builtins::BuiltinMap,
     lexers::{Key, Primitive},
diff --git a/src/lib/shell/pipe_exec/mod.rs b/src/lib/shell/pipe_exec/mod.rs
index a021d373af786a8cf81f296f9f05d596a1e11c0d..2de0033462c444d43690e4d89ab77db2d2f20840 100644
--- a/src/lib/shell/pipe_exec/mod.rs
+++ b/src/lib/shell/pipe_exec/mod.rs
@@ -223,9 +223,7 @@ impl<'b> Shell<'b> {
                 self.watch_foreground(-(pid as i32), "")
             }
             Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
-                if self.fork_function("COMMAND_NOT_FOUND", &["ion", &name]).is_err() {
-                    eprintln!("ion: command not found: {}", name);
-                }
+                self.command_not_found(name);
                 NO_SUCH_COMMAND
             }
             Err(ref err) => {
@@ -537,9 +535,7 @@ fn spawn_proc(
                     *current_pid = pid;
                 }
                 Err(ref mut err) if err.kind() == io::ErrorKind::NotFound => {
-                    if shell.fork_function("COMMAND_NOT_FOUND", &["ion", &name]).is_err() {
-                        eprintln!("ion: command not found: {}", name);
-                    }
+                    shell.command_not_found(name)
                 }
                 Err(ref mut err) => {
                     eprintln!("ion: command exec error: {}", err);