diff --git a/src/lang/bytecode.h b/src/lang/bytecode.h
index c40b897..957048b 100644
--- a/src/lang/bytecode.h
+++ b/src/lang/bytecode.h
@@ -18,6 +18,13 @@ inline const trieste::TokenDef Body{"body", trieste::flag::symtab};
 inline const trieste::TokenDef Return{"return"};
 inline const trieste::TokenDef ReturnValue{"return_value"};
 
+/// Duplicates an item on the stack. The location of the
+/// note is an index from the end of the stack, that indicates
+/// which value should be duplicate.
+///
+/// Stack: `[]::<arg_0>` -> `[]::<arg_0>::<arg_0>`
+inline const trieste::TokenDef Copy{"copy", trieste::flag::print};
+
 inline const trieste::TokenDef CreateRegion{"create_region"};
 inline const trieste::TokenDef FreezeObject{"freeze_object"};
 inline const trieste::TokenDef Null{"null"};
diff --git a/src/lang/interpreter.cc b/src/lang/interpreter.cc
index 7391383..a98257d 100644
--- a/src/lang/interpreter.cc
+++ b/src/lang/interpreter.cc
@@ -346,6 +346,22 @@ namespace verona::interpreter
         return action;
       }
 
+      if (node == Copy)
+      {
+        // This breaks the normal idea of a stack machine, but every other
+        // solution would require more effort and would be messier
+        auto copy_idx = std::stoul(std::string(node->location().view()));
+        auto stack_size = stack().size();
+        assert(copy_idx < stack_size && "the stack is too small for this copy");
+
+        auto var = stack()[stack_size - copy_idx - 1];
+        stack().push_back(var);
+        std::cout << "push " << var << std::endl;
+        rt::add_reference(frame(), var);
+
+        return ExecNext{};
+      }
+
       if (node == Return)
       {
         return ExecReturn{};
diff --git a/src/lang/lang.h b/src/lang/lang.h
index d33ad1d..149cb38 100644
--- a/src/lang/lang.h
+++ b/src/lang/lang.h
@@ -20,6 +20,7 @@ inline const TokenDef Freeze{"freeze"};
 inline const TokenDef Region{"region"};
 inline const TokenDef Lookup{"lookup"};
 inline const TokenDef Parens{"parens"};
+inline const TokenDef Method{"method"};
 
 inline const TokenDef Op{"op"};
 inline const TokenDef Rhs{"rhs"};
@@ -31,9 +32,10 @@ inline const TokenDef Compile{"compile"};
 namespace verona::wf
 {
   inline const auto lv = Ident | Lookup;
-  inline const auto rv = lv | Empty | Null | String | Create | Call;
+  inline const auto rv = lv | Empty | Null | String | Create | Call | Method;
   inline const auto cmp_values = Ident | Lookup | Null;
   inline const auto key = Ident | Lookup | String;
+  inline const auto operand = Lookup | Call | Method | Ident;
 
   inline const auto grouping = (Top <<= File) | (File <<= Body) |
     (Body <<= Block) |
@@ -45,22 +47,26 @@ namespace verona::wf
     (Freeze <<= Ident) | (Create <<= Ident) | (If <<= Eq * Block * Block) |
     (For <<= (Key >>= Ident) * (Value >>= Ident) * (Op >>= lv) * Block) |
     (Eq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) |
-    (Func <<= Ident * Params * Body) | (Call <<= (Op >>= key) * List) |
-    (ReturnValue <<= rv) | (List <<= rv++) | (Params <<= Ident++);
+    (Func <<= Ident * Params * Body) | (Call <<= Ident * List) |
+    (Method <<= Lookup * List) | (ReturnValue <<= rv) | (List <<= rv++) |
+    (Params <<= Ident++);
 
   inline const trieste::wf::Wellformed bytecode = (Top <<= Body) |
     (Body <<=
      (LoadFrame | StoreFrame | LoadField | StoreField | Drop | Null |
       CreateObject | CreateRegion | FreezeObject | IterNext | Print | Eq | Neq |
-      Jump | JumpFalse | Label | Call | Return | ReturnValue | ClearStack)++) |
+      Jump | JumpFalse | Label | Call | Return | ReturnValue | ClearStack |
+      Copy)++) |
     (CreateObject <<= (Dictionary | String | KeyIter | Proto | Func)) |
     (Func <<= Body) | (Label <<= Ident)[Ident];
 }
 
 inline const auto LV = T(Ident, Lookup);
-inline const auto RV = T(Empty, Ident, Lookup, Null, String, Create, Call);
+inline const auto RV =
+  T(Empty, Ident, Lookup, Null, String, Create, Call, Method);
 inline const auto CMP_V = T(Ident, Lookup, Null);
 inline const auto KEY = T(Ident, Lookup, String);
+inline const auto OPERAND = T(Lookup, Call, Method, Ident);
 
 // Parsing && AST construction
 Parse parser();
diff --git a/src/lang/passes/bytecode.cc b/src/lang/passes/bytecode.cc
index 42f95f2..3767372 100644
--- a/src/lang/passes/bytecode.cc
+++ b/src/lang/passes/bytecode.cc
@@ -85,6 +85,24 @@ PassDef bytecode()
                      << create_print(_(Op));
         },
 
+      T(Compile)
+          << (T(Method)[Method]
+              << ((T(Lookup)[Lookup] << (OPERAND[Op] * T(String)[Key])) *
+                  (T(List) << Any++[List]) * End)) >>
+        [](auto& _) {
+          auto arg_ctn = _[List].size();
+          return Seq
+            // `self` lookup and first argument
+            << (Compile << _(Op))
+            << (Compile << _[List])
+            // Copy self
+            << (Copy ^ std::to_string(arg_ctn))
+            // Fetch the function
+            << (Compile << _(Key))
+            << create_from(LoadField, _(Lookup))
+            // function call, +1 for the self argument
+            << (Call ^ std::to_string(arg_ctn + 1));
+        },
       T(Compile)
           << (T(Call)[Call] << (KEY[Op] * (T(List) << Any++[List]) * End)) >>
         [](auto& _) {
diff --git a/src/lang/passes/flatten.cc b/src/lang/passes/flatten.cc
index 3beb5c2..44e2cf9 100644
--- a/src/lang/passes/flatten.cc
+++ b/src/lang/passes/flatten.cc
@@ -8,13 +8,13 @@ namespace verona::wf
     (Body <<=
      (Freeze | Region | Assign | Eq | Neq | Label | Jump | JumpFalse | Print |
       StoreFrame | LoadFrame | CreateObject | Ident | IterNext | Create |
-      StoreField | Lookup | String | Call | Return | ReturnValue |
+      StoreField | Lookup | String | Call | Method | Return | ReturnValue |
       ClearStack)++) |
     (CreateObject <<= (KeyIter | String | Dictionary | Func)) |
     (Func <<= Compile) | (Compile <<= Body) | (Create <<= Ident) |
     (Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) |
     (Lookup <<= (Lhs >>= lv) * (Rhs >>= key)) | (Region <<= Ident) |
-    (Freeze <<= Ident) | (Call <<= (Op >>= key) * List) | (List <<= rv++) |
+    (Freeze <<= Ident) | (Call <<= Ident * List) | (Method <<= Lookup * List) | (List <<= rv++) |
     (Params <<= Ident++) |
     (Eq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) |
     (Neq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) |
diff --git a/src/lang/passes/grouping.cc b/src/lang/passes/grouping.cc
index 38bb55d..f8e5cb6 100644
--- a/src/lang/passes/grouping.cc
+++ b/src/lang/passes/grouping.cc
@@ -42,6 +42,19 @@ PassDef grouping()
 
           return create_from(Call, _(Group)) << _(Ident) << list;
         },
+      --In(Method) *
+          (T(Group)[Group]
+           << ((T(Lookup)[Lookup]) * (T(Parens)[Parens] << (~T(List)[List])) *
+               End)) >>
+        [](auto& _) {
+          auto list = _(List);
+          if (!list)
+          {
+            list = create_from(List, _(Parens));
+          }
+
+          return create_from(Method, _(Group)) << _(Lookup) << list;
+        },
 
       T(Group) << ((T(Create)[Create] << End) * T(Ident)[Ident] * End) >>
         [](auto& _) {
@@ -89,7 +102,9 @@ PassDef grouping()
           << ((T(Group) << End) *
               (T(Group)
                << ((T(Ident)[Ident]) *
-                   (T(Parens)[Parens] << (~(T(List) << T(Ident)++[List]))) *
+                   (T(Parens)[Parens] << (
+                      // (T(Group) << T(Ident)[List]) /
+                      ~(T(List) << T(Ident)++[List]))) *
                    End)) *
               (T(Group) << T(Block)[Block]) * End) >>
         [](auto& _) {
@@ -97,6 +112,12 @@ PassDef grouping()
             << _(Ident) << (create_from(Params, _(Parens)) << _[List])
             << (Body << _(Block));
         },
+      // Normalize functions with a single ident to also have a list token
+      T(Parens)[Parens] << (T(Group) << (T(Ident)[Ident] * End)) >>
+        [](auto& _) {
+          return create_from(Parens, _(Parens))
+            << (create_from(List, _(Parens)) << _(Ident));
+        },
 
       T(Return)[Return] << ((T(Group) << End) * End) >>
         [](auto& _) { return create_from(Return, _(Return)); },
diff --git a/tests/self.vpy b/tests/self.vpy
new file mode 100644
index 0000000..844d7a5
--- /dev/null
+++ b/tests/self.vpy
@@ -0,0 +1,11 @@
+
+def str_name(self):
+    return "String"
+
+value = "Music :3"
+value.name = str_name
+drop str_name
+
+name = value.name()
+
+