>>> from Redy.ADT.Core import data
    >>> from Redy.Magic.Classic import cast
    >>> from Redy.ADT.traits import ConsInd, Discrete
    >>> @data
    >>> class S(ConsInd):  # ConsInd class makes it available to index from this data as from a tuple.
    >>>    a: '1'
    >>>    b: lambda x: x
    >>>    c: lambda x, y : ...


    >>> assert isinstance(S.a, S)
    >>> assert isinstance(S.b('2'), S)
    >>> assert S.b('2').__str__() == "2(\'2\')"
    >>> assert S.c(1, 2).__str__() == 'c(1, 2)'

    >>> assert S.c(1, 2)[:] == (S.c, 1, 2)  # here `ConsInd` works!

    # Nat
    >>> @data
    >>> class Nat(Discrete, ConsInd):
    >>>     Zero : ...
    >>>     Succ : lambda o: ...
    >>>     def _to_nat(self):
    >>>         if self is Nat.Zero:
    >>>             return 0
    >>>         return 1 + self[1]._to_nat()
    >>>     @cast(str)
    >>>     def __str__(self):
    >>>         return f'Nat[{self._to_nat()}]'
    >>> print (Nat.Succ((Nat.Succ(Nat.Zero))))
    >>> assert Nat.Zero == Nat.Zero
    >>> assert len({Nat.Zero, Nat.Succ(Nat.Zero), Nat.Succ(Nat.Zero)}) is 2

def data(cls_def: type):
    """
    >>> from Redy.ADT.Core import data
    >>> from Redy.Magic.Classic import cast
    >>> from Redy.ADT.traits import ConsInd, Discrete
    >>> @data
    >>> class S(ConsInd):  # ConsInd class makes it available to index from this data as from a tuple.
    >>>    a: '1'
    >>>    b: lambda x: x
    >>>    c: lambda x, y : ...


    >>> assert isinstance(S.a, S)
    >>> assert isinstance(S.b('2'), S)
    >>> assert S.b('2').__str__() == "2(\'2\')"
    >>> assert S.c(1, 2).__str__() == 'c(1, 2)'

    >>> assert S.c(1, 2)[:] == (S.c, 1, 2)  # here `ConsInd` works!

    # Nat
    >>> @data
    >>> class Nat(Discrete, ConsInd):
    >>>     Zero : ...
    >>>     Succ : lambda o: ...
    >>>     def _to_nat(self):
    >>>         if self is Nat.Zero:
    >>>             return 0
    >>>         return 1 + self[1]._to_nat()
    >>>     @cast(str)
    >>>     def __str__(self):
    >>>         return f'Nat[{self._to_nat()}]'
    >>> print (Nat.Succ((Nat.Succ(Nat.Zero))))
    >>> assert Nat.Zero == Nat.Zero
    >>> assert len({Nat.Zero, Nat.Succ(Nat.Zero), Nat.Succ(Nat.Zero)}) is 2
    """
    __annotations__ = cls_def.__annotations__

    def constructor_descriptor(f):
        return discrete_cache(f) if issubclass(cls_def, Discrete) else f

    __dict__ = {}
    for each in reversed(cls_def.__mro__):
        __dict__.update({k: v for k, v in each.__dict__.items() if v})

    new_cls_def = type(cls_def.__name__, cls_def.__bases__, __dict__)

    new_cls_def.__slots__ = ['__sig_str__', '__structure__']

    if not hasattr(new_cls_def, '__repr__') or isinstance(new_cls_def.__repr__, BuiltinMethod):
        def __repr__(self):
            s = self.__structure__
            if not isinstance(s, tuple):
                return self.__sig_str__
            return f'{self.__sig_str__}({", ".join(tuple(map(repr, self.__structure__[1:])))})'

        new_cls_def.__repr__ = __repr__

    @property
    def __inst_str__(self):
        return self.__sig_str__

    @__inst_str__.setter
    def __inst_str__(self, value):
        warnings.warn('`__inst_str__` of ADT will not removed after Redy 0.3!')
        self.__sig_str__ = value

    new_cls_def.__inst_str__ = __inst_str__

    def __destruct__(self):
        return self.__structure__

    def __iter__(self):
        yield from self.__structure__

    new_cls_def.__destruct__ = __destruct__
    new_cls_def.__iter__ = __iter__

    def make_type(str_value: object, default: str):
        entity = new_cls_def()
        entity.__sig_str__ = default if str_value is ... else str_value
        entity.__structure__ = None

        return entity

    # if Discrete, it means that for a definite input, the return of data constructor is definitely the same object.

    for each, annotation in __annotations__.items():
        if callable(annotation):
            spec = getfullargspec(annotation)
            if isinstance(annotation, DTTransDescriptor):
                _args = ", ".join(getargs(annotation.func.__code__).args)
                code: types.CodeType = annotation.func.__code__
                impl = (f"def call({_args}):\n"
                        f"    entity = new_cls_def()\n"
                        f"    __structure__, __sig_str__ = annotation.func({_args})\n"
                        f"    entity.__sig_str__ = {repr(each)} if __sig_str__  is ... else __sig_str__\n"
                        f"    entity.__structure__ = cons, *__structure__\n"
                        f"    return entity\n")
            else:
                _args = ", ".join(getargs(annotation.__code__).args)
                code: types.CodeType = annotation.__code__
                impl = (f"def call({_args}):\n"
                        f"    entity = new_cls_def()\n"
                        f"    str_value = annotation({_args})\n"
                        f"    entity.__sig_str__ = {repr(each)} if str_value is ... else str_value\n"
                        f"    entity.__structure__ = cons, {_args}\n"
                        f"    return entity")
            if spec.defaults or spec.kwonlyargs or spec.varkw:
                raise TypeError('An ADT constructor must be a lambda without default args or keyword args.')

            scope = {'annotation': annotation, 'new_cls_def': new_cls_def}
            exec(compile(f"{impl}", "Redy.ADT.Core.py", "exec", 0, False), scope)
            _singleton_inst = scope['call']

            maker = new_func_maker(_singleton_inst)
            _singleton_inst = maker(filename=code.co_filename, firstlineno=code.co_firstlineno, lnotab=code.co_lnotab, name=each)

            singleton_inst = constructor_descriptor(_singleton_inst)
            scope['cons'] = singleton_inst

        else:
            singleton_inst = make_type(annotation, each)

        setattr(new_cls_def, each, singleton_inst)

    return new_cls_def