diff --git a/core/builtin_funcs.py b/core/builtin_funcs.py
index baf8eab..2fda55c 100755
--- a/core/builtin_funcs.py
+++ b/core/builtin_funcs.py
@@ -98,6 +98,26 @@ def execute_print(self, exec_ctx: Context) -> RTResult[Value]:
     def execute_print_ret(self, exec_ctx: Context) -> RTResult[Value]:
         return RTResult[Value]().success(String(str(exec_ctx.symbol_table.get("value"))))
 
+    @args(["value"])
+    def execute_len(self, exec_ctx: Context) -> RTResult[Value]:
+        val = exec_ctx.symbol_table.get("value")
+        try:
+            if val is not None and val.__class__ is not Value:
+                if hasattr(val, "__len__"):
+                    ret = int(val.__len__())
+                elif hasattr(val, "__exec_len__"):
+                    ret = int(val.__exec_len__())
+                else:
+                    raise TypeError()
+                return RTResult[Value]().success(Number(ret))
+            raise TypeError()
+        except TypeError:
+            return RTResult[Value]().failure(
+                Error(
+                    self.pos_start, self.pos_end, "TypeError", f'Object of type "{val.__class__.__name__}" has no len()'
+                )
+            )
+
     @args(["value"])
     def execute_input(self, exec_ctx: Context) -> RTResult[Value]:
         text = input(str(exec_ctx.symbol_table.get("value")))
@@ -552,6 +572,7 @@ def create_global_symbol_table() -> SymbolTable:
     ret.set("cls", BuiltInFunction("clear"))
     ret.set("require", BuiltInFunction("require"))
     ret.set("exit", BuiltInFunction("exit"))
+    ret.set("len", BuiltInFunction("len"))
     # Datatype validator methods
     ret.set("is_num", BuiltInFunction("is_num"))
     ret.set("is_int", BuiltInFunction("is_int"))
diff --git a/core/datatypes.py b/core/datatypes.py
index 96b056e..6b4abcc 100755
--- a/core/datatypes.py
+++ b/core/datatypes.py
@@ -134,6 +134,9 @@ def __init__(self, generator: Generator[RTResult[Value], None, None]) -> None:
         super().__init__()
         self.it = generator
 
+    def __len__(self) -> int:
+        return len(list(self.it))
+
     def iter(self) -> Iterator:
         return self
 
@@ -334,6 +337,9 @@ def copy(self) -> Boolean:
     def is_true(self) -> bool:
         return self.value
 
+    def __len__(self) -> int:
+        return 1 if self.value else 0
+
     def __str__(self) -> str:
         return "true" if self.value else "false"
 
@@ -625,7 +631,7 @@ def set_index(self, index: Value, value: Value) -> ResultTuple:
         return self, None
 
     def contains(self, value: Value) -> ResultTuple:
-        ret = Boolean.false()
+        ret: Boolean = Boolean.false()
         for val in self.elements:
             cmp, err = val.get_comparison_eq(value)
             if err is not None:
@@ -640,21 +646,21 @@ def is_true(self) -> bool:
         return len(self.elements) > 0
 
     def copy(self) -> Array:
-        copy = Array(self.elements)
+        copy: Array = Array(self.elements)
         copy.set_pos(self.pos_start, self.pos_end)
         copy.set_context(self.context)
         return copy
 
-    def __str__(self):
+    def __str__(self) -> str:
         return self.__repr__()
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return f'[{", ".join(repr(x) for x in self.elements)}]'
 
     def __iter__(self):
         return iter(self.elements)
 
-    def __getitem__(self, index):
+    def __getitem__(self, index) -> Value:
         return self.elements[index]
 
     def __len__(self):
@@ -755,6 +761,9 @@ def get_comparison_ne(self, other: Value) -> ResultTuple:
 
         return Boolean.false(), None
 
+    def __len__(self) -> int:
+        return len(self.values)
+
     def copy(self) -> HashMap:
         copy = HashMap(self.values)
         copy.set_pos(self.pos_start, self.pos_end)
@@ -1099,6 +1108,12 @@ class Instance(BaseInstance):
     def __init__(self, parent_class: Class) -> None:
         super().__init__(parent_class, None)
 
+    def __exec_len__(self):
+        try:
+            return self.operator("__len__")[0].value
+        except AttributeError:
+            return Null.null()
+
     def bind_method(self, method: BaseFunction) -> RTResult[BaseFunction]:
         method = method.copy()
         if method.symbol_table is None:
diff --git a/stdlib/array.rn b/stdlib/array.rn
index 6d7cc89..f032382 100644
--- a/stdlib/array.rn
+++ b/stdlib/array.rn
@@ -6,7 +6,7 @@ class Array {
     fun map(func) {
         new_elements = []
 
-        for i = 0 to this.len() {
+        for i = 0 to this.__len__() {
             arr_append(new_elements, func(arr_get(this.list, i)))
         }
 
@@ -22,7 +22,7 @@ class Array {
         return (this.list)[start:end]
     }
     
-    fun len() -> arr_len(this.list)
+    fun __len__() -> arr_len(this.list)
     fun is_empty() -> this.list == []
     fun to_string() -> str(this.list)
     fun is_array() -> true
diff --git a/tests/len.rn b/tests/len.rn
new file mode 100644
index 0000000..248b488
--- /dev/null
+++ b/tests/len.rn
@@ -0,0 +1,21 @@
+class Test
+{
+    fun __constructor__(){};
+    fun __len__()
+    {
+        return 10
+    }
+}
+
+print(len(Test())) # 10
+print(len("test")) # 4
+print(len([1,2,3])) # 3
+print(len({"some": 123, "this": "ok"}))
+print(len(true))
+print(len(false))
+
+try {
+    print(len(234))
+} catch as err {
+    print(err) # TypeError
+}
\ No newline at end of file
diff --git a/tests/len.rn.json b/tests/len.rn.json
new file mode 100644
index 0000000..aaf8684
--- /dev/null
+++ b/tests/len.rn.json
@@ -0,0 +1 @@
+{"code": 0, "stdout": "10\n4\n3\n2\n1\n0\nTypeError: Object of type \"Number\" has no len()\n", "stderr": ""}
\ No newline at end of file