Idea:
Once a class is created i want it to be immutable, but i dont want to re-write every time the “initial conditions”, so i create a decorator that overwrites the __setattr__ function of a class, and adds an update function where it creates a copy of the class with only some of the fields changed
Code:
from dataclasses import dataclass
import copy
class Haskell_Class():
def __init__(self):
self.__haskell_class__ = True
def __setattr__(self, name, value):
try:
if self.__haskell_class__ == True: return
except AttributeError: self.__dict__[name] = value
def freze_after_initialization(self, name, value):
try:
if self.__haskell_class__ == True: return
except AttributeError: self.__dict__[name] = value
def haskell_class(cls):
def wrapper(*args, **kwargs):
new_class_instance = cls(*args, **kwargs)
new_class_instance.__haskell_class__ = True
setattr(new_class_instance, "__setattr__", freze_after_initialization)
return new_class_instance
return wrapper
def is_haskell_class(cls):
try: return cls.__haskell_class__
except AttributeError: return False
def haskell_class(cls):
def new_init(func):
def wrapper(self, *args, **kwargs):
func(self, *args, **kwargs)
setattr(self, "__haskell_class__", True)
return wrapper
def freze_after_initialization(self, name, value):
if is_haskell_class(self) and name != "__haskell_class__":
if self.__haskell_class__ == True:
err_msg = "\n\tClass is defined as an HASKELL_CLASS"
err_msg += "\n\tTo change a value use the function update(...)"
raise AttributeError(err_msg)
self.__dict__[name] = value
def update(self, **kwargs):
new_class_instance = copy.deepcopy(self)
new_class_instance.__haskell_class__ = False
for key in kwargs:
if key not in new_class_instance.__dict__:
err_msg = f"{key} is not an instance of the class"
raise AttributeError(err_msg)
new_class_instance.__dict__[key] = kwargs[key]
new_class_instance.__haskell_class__ = True
return new_class_instance
cls.__init__ = new_init(cls.__init__)
cls.__setattr__ = freze_after_initialization
cls.update = update
return cls
def Test():
@haskell_class
@dataclass
class PORVA():
a : int
b : int
print("START")
a = Haskell_Class()
b = PORVA(3, 4)
print(b.a)
print(b.update(a = 1, b = 3))
print(b)
if __name__ == '__main__':
Test()