diff --git a/monads/list.py b/monads/list.py
index 070e7ff..2a21519 100644
--- a/monads/list.py
+++ b/monads/list.py
@@ -27,15 +27,15 @@ class List(Monad[T], Monoidal[list]):
         return List([value])
 
     def bind(self, function: Callable[[T], List[S]]) -> List[S]:
-        return reduce(List.mappend, map(function, self.value), List.mzero())
+        return reduce(List.mappend, map(function, self._value), List.mzero())
 
     def map(self, function: Callable[[T], S]) -> List[S]:
-        return List(list(map(function, self.value)))
+        return List(list(map(function, self._value)))
 
     def apply(self, functor: List[Callable[[T], S]]) -> List[S]:
 
         return List(
-            list(chain.from_iterable([map(f, self.value) for f in functor.value]))
+            list(chain.from_iterable([map(f, self._value) for f in functor._value]))
         )
 
     @classmethod
@@ -64,7 +64,7 @@ class List(Monad[T], Monoidal[list]):
         return List(reduce(flat, self, List.mzero()))  # type: ignore
 
     def sort(self, key: Optional[str] = None, reverse: bool = False) -> List[T]:
-        lst_copy = self.value.copy()
+        lst_copy = self._value.copy()
         lst_copy.sort(key=key, reverse=reverse)  # type: ignore
         return List(lst_copy)
 
@@ -75,22 +75,22 @@ class List(Monad[T], Monoidal[list]):
             functor = uncurry(cast(CurriedBinary, func))
         else:
             functor = func
-        return reduce(functor, self.value, base_val)  # type: ignore
+        return reduce(functor, self._value, base_val)  # type: ignore
 
     __and__ = lambda other, self: List.apply(self, other)  # type: ignore
 
     def mappend(self, other: List[T]) -> List[T]:
-        return List(self.value + other.value)
+        return List(self._value + other._value)
 
     __add__ = mappend
     __mul__ = __rmul__ = map
     __rshift__ = bind
 
     def __sizeof__(self) -> int:
-        return self.value.__sizeof__()
+        return self._value.__sizeof__()
 
     def __len__(self) -> int:
-        return len(list(self.value))
+        return len(list(self._value))
 
     def __iter__(self) -> Iterator[T]:
-        return iter(self.value)
+        return iter(self._value)
diff --git a/monads/maybe.py b/monads/maybe.py
index 9ac2e6b..c320a97 100644
--- a/monads/maybe.py
+++ b/monads/maybe.py
@@ -176,19 +176,19 @@ class First(Monoid[Maybe[T]]):
         return First(Nothing())
 
     def mappend(self, other: First):
-        if isinstance(self.value, Just):
+        if isinstance(self._value, Just):
             return self
         else:
             return other
 
     def __repr__(self) -> str:  # pragma: no cover
-        return f"<First {self.value}>"
+        return f"<First {self._value}>"
 
     __add__ = mappend
 
 
 def first(xs: List[Maybe[T]]) -> Maybe[T]:
-    return First.mconcat(map(lambda x: First(x), xs)).value
+    return First.mconcat(map(lambda x: First(x), xs))._value
 
 
 class Last(Monoid[Maybe[T]]):
@@ -197,16 +197,16 @@ class Last(Monoid[Maybe[T]]):
         return Last(Nothing())
 
     def mappend(self, other: Last):
-        if isinstance(other.value, Just):
+        if isinstance(other._value, Just):
             return other
         else:
             return self
 
     def __repr__(self) -> str:  # pragma: no cover
-        return f"<Last {self.value}>"
+        return f"<Last {self._value}>"
 
     __add__ = mappend
 
 
 def last(xs: List[Maybe[T]]) -> Maybe[T]:
-    return Last.mconcat(map(lambda x: Last(x), xs)).value
+    return Last.mconcat(map(lambda x: Last(x), xs))._value
diff --git a/monads/monoid.py b/monads/monoid.py
index be85eeb..716697c 100644
--- a/monads/monoid.py
+++ b/monads/monoid.py
@@ -2,14 +2,14 @@ from __future__ import annotations
 from functools import reduce
 from numbers import Complex
 from decimal import Decimal
-from typing import Any, Callable, Generic, Iterator, Type, TypeVar, Union
+from typing import Any, Callable, Generic, Iterator, Type, TypeVar, Union, Final
 
 T = TypeVar("T")
 
 
 class Monoid(Generic[T]):
     def __init__(self, value: T) -> None:
-        self.value = value
+        self._value: Final[T] = value
 
     # FIXME: Other type set to Any, as the proper value (Monoid[T]) is
     # reported as incompatible with subclass implementations due to a
@@ -29,15 +29,19 @@ class Monoid(Generic[T]):
         return (
             isinstance(other, Monoid)
             and type(self) == type(other)
-            and self.value == other.value
+            and self._value == other._value
         )
 
     __add__ = mappend
 
+    @property
+    def value(self) -> T:
+        return self._value
+
 
 class Monoidal(Monoid[T]):
     def __repr__(self):  # pragma: no cover
-        return repr(self.value)
+        return repr(self._value)
 
 
 class String(Monoidal[str]):
@@ -46,7 +50,7 @@ class String(Monoidal[str]):
         return cls(str())
 
     def mappend(self, other: String) -> String:
-        return String(self.value + other.value)
+        return String(self._value + other._value)
 
     __add__ = mappend
 
@@ -57,7 +61,7 @@ class Addition(Monoidal[Union[int, float]]):
         return cls(0)
 
     def mappend(self, other: Addition) -> Addition:
-        return Addition(self.value + other.value)
+        return Addition(self._value + other._value)
 
     __add__ = mappend
 
@@ -68,6 +72,6 @@ class Multiplication(Monoidal[Union[int, float]]):
         return cls(1)
 
     def mappend(self, other: Multiplication) -> Multiplication:
-        return Multiplication(self.value * other.value)
+        return Multiplication(self._value * other._value)
 
     __add__ = mappend
diff --git a/monads/set.py b/monads/set.py
index 4bb4454..498b42b 100644
--- a/monads/set.py
+++ b/monads/set.py
@@ -38,15 +38,15 @@ class Set(Monad[T], Monoidal[set]):
         return Set(unpack(value))
 
     def bind(self, function: Callable[[T], Set[S]]) -> Set[S]:
-        return reduce(Set.mappend, map(function, self.value), Set.mzero())
+        return reduce(Set.mappend, map(function, self._value), Set.mzero())
 
     def map(self, function: Callable[[T], S]) -> Set[S]:
-        return Set(set(map(function, self.value)))
+        return Set(set(map(function, self._value)))
 
     def apply(self, functor: Set[Callable[[T], S]]) -> Set[S]:
 
         return Set(
-            set(chain.from_iterable([map(f, self.value) for f in functor.value]))
+            set(chain.from_iterable([map(f, self._value) for f in functor._value]))
         )
 
     @classmethod
@@ -75,7 +75,7 @@ class Set(Monad[T], Monoidal[set]):
         return Set(reduce(flat, self, Set.mzero()))  # type: ignore
 
     def sort(self, key: Optional[str] = None, reverse: bool = False) -> Set[T]:
-        lst_copy = self.value.copy()
+        lst_copy = self._value.copy()
         lst_copy.sort(key=key, reverse=reverse)  # type: ignore
         return Set(lst_copy)
 
@@ -86,22 +86,22 @@ class Set(Monad[T], Monoidal[set]):
             functor = uncurry(cast(CurriedBinary, func))
         else:
             functor = func
-        return reduce(functor, self.value, base_val)  # type: ignore
+        return reduce(functor, self._value, base_val)  # type: ignore
 
     __and__ = lambda other, self: Set.apply(self, other)  # type: ignore
 
     def mappend(self, other: Set[T]) -> Set[T]:
-        return Set(self.value.union(other.value))
+        return Set(self._value.union(other._value))
 
     __add__ = mappend
     __mul__ = __rmul__ = map
     __rshift__ = bind
 
     def __sizeof__(self) -> int:
-        return self.value.__sizeof__()
+        return self._value.__sizeof__()
 
     def __len__(self) -> int:
-        return len(set(self.value))
+        return len(set(self._value))
 
     def __iter__(self) -> Iterator[T]:
-        return iter(self.value)
+        return iter(self._value)
diff --git a/tests/test_monoids.py b/tests/test_monoids.py
index 927a11c..78161dc 100644
--- a/tests/test_monoids.py
+++ b/tests/test_monoids.py
@@ -55,3 +55,11 @@ def test_mconcat(constructor: Constructor) -> None:
     c: Monoid = construct(constructor, 3)
     expected: Monoid = a.mappend(b).mappend(c)
     assert expected == cls.mconcat([a, b, c])
+
+
+def test_immutability(constructor: Constructor) -> None:
+    a: Monoid = construct(constructor, 1)
+    with pytest.raises(AttributeError) as excinfo:
+        # this is ignore on porpouse othewise the mypy test fail. Uncomment to check the Final check with mypy
+        a.value = 2  # type: ignore
+    assert "can't set attribute" in str(excinfo.value)