diff --git a/projects/shoggoth/src/python/ichor/__init__.py b/projects/shoggoth/src/python/ichor/__init__.py
index ab10030..375fa7c 100644
--- a/projects/shoggoth/src/python/ichor/__init__.py
+++ b/projects/shoggoth/src/python/ichor/__init__.py
@@ -1,5 +1,12 @@
-# noqa
+#!/usr/bin/env python
 
-from .bootstrap import *  # noqa
-from .impl import *  # noqa
-from .isa import *  # noqa
+"""
+Import everything out of the Ichor modules.
+
+FIXME: A real public API here would be great, but somewhat un-pythonic.
+"""
+
+from ichor.bootstrap import *  # noqa
+from ichor.impl import *  # noqa
+from ichor.isa import *  # noqa
+from ichor.typing import *  # noqa
diff --git a/projects/shoggoth/src/python/ichor/bootstrap.py b/projects/shoggoth/src/python/ichor/bootstrap.py
index 027d295..dbeed0b 100644
--- a/projects/shoggoth/src/python/ichor/bootstrap.py
+++ b/projects/shoggoth/src/python/ichor/bootstrap.py
@@ -4,8 +4,8 @@ Some utterly trivial functions and types that allow me to begin testing the VM.
 Hopefully no "real" interpreter ever uses this code, since it's obviously replaceable.
 """
 
-from .isa import Module, Opcode
-from .typing import ProductExpr, SumExpr
+from ichor.isa import Opcode
+from ichor.state import Module
 
 
 BOOTSTRAP = Module()
diff --git a/projects/shoggoth/src/python/ichor/impl.py b/projects/shoggoth/src/python/ichor/impl.py
index 6dce03b..832e757 100644
--- a/projects/shoggoth/src/python/ichor/impl.py
+++ b/projects/shoggoth/src/python/ichor/impl.py
@@ -9,16 +9,10 @@ context (a virtual machine) which DOES have an easily introspected and serialize
 """
 
 
-import sys
-
-from ichor.typing import Identifier
-
-
-assert sys.version_info > (3, 10, 0), "`match` support is required"
-
 from copy import deepcopy
 
-from .isa import Closure, FunctionRef, Opcode, Identifier
+from ichor.isa import Opcode
+from ichor.typing import Closure, FunctionRef, Identifier
 
 
 def rotate(l):
@@ -39,7 +33,7 @@ class Stackframe(object):
     def pop(self):
         return self.stack.pop(0)
 
-    def call(self, signature: FunctionRef, ip):
+    def call(self, signature: FunctionRef, ip) -> "Stackframe":
         self.ip += 1
         nargs = len(signature.args)
         args, self.stack = self.stack[:nargs], self.stack[nargs:]
@@ -51,7 +45,7 @@ class Stackframe(object):
             depth=self.depth+1
         )
 
-    def ret(self, nargs):
+    def ret(self, nargs) -> "Stackframe":
         self.parent.stack = self.stack[:nargs] + self.parent.stack
         return self.parent
 
@@ -88,95 +82,95 @@ class Interpreter(object):
     def run(self, opcodes, stack=[]):
         """Directly interpret some opcodes in the configured environment."""
 
-        stack = Stackframe(stack=stack)
+        stackframe = Stackframe(stack=stack)
         mod = self.bootstrap.copy()
-        stack.ip = mod.functions[mod.define_function(";<main>;;", opcodes)]
+        stackframe.ip = mod.functions[mod.define_function(";<main>;;", opcodes)]
 
         print(mod)
 
         def _error(msg=None):
             # Note this is pretty expensive because we have to snapshot the stack BEFORE we do anything
             # And the stack object isn't immutable or otherwise designed for cheap snapshotting
-            raise InterpreterError(mod, deepcopy(stack), msg)
+            raise InterpreterError(mod, deepcopy(stackframe), msg)
 
         while True:
-            op = mod.opcodes[stack.ip]
-            print("{0}{1: <50} {2}: {3}".format("  " * stack.depth, str(stack.stack), stack.ip, op))
+            op = mod.opcodes[stackframe.ip]
+            print("{0}{1: <50} {2}: {3}".format("  " * stackframe.depth, str(stackframe.stack), stackframe.ip, op))
 
             match op:
                 case Opcode.TRUE():
-                    stack.push(True)
+                    stackframe.push(True)
 
                 case Opcode.FALSE():
-                    stack.push(False)
+                    stackframe.push(False)
 
                 case Opcode.IF(target):
-                    if len(stack) < 1:
+                    if len(stackframe) < 1:
                         _error("Stack size violation")
 
-                    val = stack.pop()
+                    val = stackframe.pop()
                     if val not in [True, False]:
                         _error("Type violation")
 
                     if val is False:
-                        stack.ip = target
+                        stackframe.ip = target
                         continue
 
                 case Opcode.GOTO(n):
                     if (n < 0):
                         _error("Illegal branch target")
 
-                    stack.ip = n
+                    stackframe.ip = n
                     continue
 
                 case Opcode.DUP(n):
-                    if (n > len(stack)):
+                    if (n > len(stackframe)):
                         _error("Stack size violation")
 
-                    stack.dup(n)
+                    stackframe.dup(n)
 
                 case Opcode.ROT(n):
-                    if (n > len(stack)):
+                    if (n > len(stackframe)):
                         _error("Stack size violation")
 
-                    stack.rot(n)
+                    stackframe.rot(n)
 
                 case Opcode.DROP(n):
-                    if (n > len(stack)):
+                    if (n > len(stackframe)):
                         _error("Stack size violation")
 
-                    stack.drop(n)
+                    stackframe.drop(n)
 
                 case Opcode.SLOT(n):
                     if (n < 0):
                         _error("SLOT must have a positive reference")
-                    if (n > len(stack.stack) - 1):
+                    if (n > len(stackframe.stack) - 1):
                         _error("SLOT reference out of range")
-                    stack.push(stack.stack[len(stack) - n - 1])
+                    stackframe.push(stackframe.stack[len(stackframe) - n - 1])
 
                 case Opcode.IDENTIFIERC(name):
                     if not (name in mod.functions or name in mod.types):
                         _error("IDENTIFIERC references unknown entity")
 
-                    stack.push(Identifier(name))
+                    stackframe.push(Identifier(name))
 
                 case Opcode.FUNREF():
-                    id = stack.pop()
+                    id = stackframe.pop()
                     if not isinstance(id, Identifier):
                         _error("FUNREF consumes an IDENTIFIER")
                     try:
                         # FIXME: Verify this statically
-                        stack.push(FunctionRef.parse(id.name))
+                        stackframe.push(FunctionRef.parse(id.name))
                     except:
                         _error("Invalid function ref")
 
                 case Opcode.CALLF(n):
-                    sig = stack.pop()
+                    sig = stackframe.pop()
                     if not isinstance(sig, FunctionRef):
                         _error("CALLF requires a funref at top of stack")
                     if n != len(sig.args):
                         _error("CALLF target violation; argument count missmatch")
-                    if n > len(stack):
+                    if n > len(stackframe):
                         _error("Stack size violation")
 
                     try:
@@ -184,69 +178,69 @@ class Interpreter(object):
                     except KeyError:
                         _error("Unknown target")
 
-                    stack = stack.call(sig, ip)
+                    stackframe = stackframe.call(sig, ip)
                     continue
 
                 case Opcode.RETURN(n):
-                    if (n > len(stack)):
+                    if (n > len(stackframe)):
                         _error("Stack size violation")
 
-                    if stack.depth == 0:
-                        return stack[:n]
+                    if stackframe.depth == 0:
+                        return stackframe[:n]
 
-                    sig = FunctionRef.parse(stack.name)
+                    sig = FunctionRef.parse(stackframe.name)
                     if (len(sig.ret) != n):
                         _error("Signature violation")
 
-                    stack = stack.ret(n)
+                    stackframe = stackframe.ret(n)
                     continue
 
                 case Opcode.CLOSUREF(n):
-                    sig = stack.pop()
+                    sig = stackframe.pop()
                     if not isinstance(sig, FunctionRef):
                         _error("CLOSUREF requires a funref at top of stack")
                     if not n <= len(sig.args):
                         _error("CLOSUREF target violation; too many parameters provided")
-                    if n > len(stack):
+                    if n > len(stackframe):
                         _error("Stack size violation")
 
                     c = Closure(
                         sig,
-                        stack.stack[:n]
+                        stackframe.stack[:n]
                     )
-                    stack.drop(n)
-                    stack.push(c)
+                    stackframe.drop(n)
+                    stackframe.push(c)
 
                 case Opcode.CLOSUREC(n):
-                    c = stack.pop()
+                    c = stackframe.pop()
                     if not isinstance(c, Closure):
                         _error("CLOSUREC requires a closure at top of stack")
                     if n + len(c.frag) > len(c.funref.args):
                         _error("CLOSUREC target violation; too many parameters provided")
-                    if n > len(stack):
+                    if n > len(stackframe):
                         _error("Stack size violation")
 
                     c = Closure(
                         c.funref,
-                        stack.stack[:n] + c.frag
+                        stackframe.stack[:n] + c.frag
                     )
-                    stack.drop(n)
-                    stack.push(c)
+                    stackframe.drop(n)
+                    stackframe.push(c)
 
                 case Opcode.CALLC(n):
-                    c = stack.pop()
+                    c = stackframe.pop()
                     if not isinstance(c, Closure):
                         _error("CALLC requires a closure at top of stack")
                     if n + len(c.frag) != len(c.funref.args):
                         _error("CALLC target vionation; argument count missmatch")
-                    if n > len(stack):
+                    if n > len(stackframe):
                         _error("Stack size violation")
 
                     # Extract the function signature
                     sig = c.funref
 
                     # Push the closure's stack fragment
-                    stack.stack = c.frag + stack.stack
+                    stackframe.stack = c.frag + stackframe.stack
 
                     # Perform a "normal" funref call
                     try:
@@ -254,28 +248,10 @@ class Interpreter(object):
                     except KeyError:
                         _error("Unknown target")
 
-                    stack = stack.call(sig, ip)
+                    stackframe = stackframe.call(sig, ip)
                     continue
 
-                case Opcode.STRUCT(structref, n):
-                    if not (t := module.types.get(structref)):
-                        _error(f"STRUCT must reference a known type, {structref!r} is undefined")
-                    if n > len(stack):
-                        _error("Stack size violation")
-
-                    args = stack[:n]
-                    stack.drop(n)
-
-                    # FIXME: typecheck
-                    for arg, ftype in zip(args, t.children):
-                        pass
-
-                    inst = Struct(structref, [], dict(zip(t.field_names, args)))
-
-                    stack.push(inst)
-
-
                 case _:
                     raise Exception(f"Unhandled interpreter state {op}")
 
-            stack.ip += 1
+            stackframe.ip += 1
diff --git a/projects/shoggoth/src/python/ichor/isa.py b/projects/shoggoth/src/python/ichor/isa.py
index 0b5c72b..c65b5d5 100644
--- a/projects/shoggoth/src/python/ichor/isa.py
+++ b/projects/shoggoth/src/python/ichor/isa.py
@@ -3,8 +3,6 @@
 
 import typing as t
 
-from .typing import *
-
 
 class Opcode:
     ####################################################################################################
@@ -231,61 +229,3 @@ class Opcode:
     class BREAK(t.NamedTuple):
         """Abort the interpreter."""
         pass
-
-
-class Module(t.NamedTuple):
-    opcodes: list = []
-    functions: dict = {}
-    types: dict = {}
-    constants: dict = {}
-
-    def copy(self):
-        return Module(
-            self.opcodes.copy(),
-            self.functions.copy(),
-            self.types.copy(),
-            self.constants.copy(),
-        )
-
-    @staticmethod
-    def translate(start: int, end: int, i: "Opcode"):
-        # FIXME: Consolidate bounds checks somehow
-        match i:
-            case Opcode.IF(t):
-                d = t + start
-                assert start <= d < end
-                return Opcode.IF(d)
-
-            case Opcode.GOTO(t):
-                d = t + start
-                assert start <= d < end
-                return Opcode.GOTO(d)
-
-            case _:
-                return i
-
-    def define_function(self, name, opcodes):
-        try:
-            sig = FunctionRef.parse(name)
-            assert sig.name
-        except:
-            raise ValueError("Illegal name provided")
-
-        start = len(self.opcodes)
-        self.functions[name] = start
-        for op in opcodes:
-            self.opcodes.append(self.translate(start, start + len(opcodes), op))
-        return name
-
-    def define_type(self, name, signature):
-        self.types[name] = signature
-        return name
-
-    def __str__(self):
-        b = []
-        marks = {v: k for k, v in self.functions.items()}
-        for i, o in zip(range(1<<64), self.opcodes):
-            if(i in marks):
-                b.append(f"{marks[i]}:")
-            b.append(f"{i: >10}: {o}")
-        return "\n".join(b)
diff --git a/projects/shoggoth/test/python/ichor/fixtures.py b/projects/shoggoth/test/python/ichor/fixtures.py
index 0d12c13..5257a3d 100644
--- a/projects/shoggoth/test/python/ichor/fixtures.py
+++ b/projects/shoggoth/test/python/ichor/fixtures.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-from ichor import *
+from ichor import Interpreter, BOOTSTRAP
 import pytest