首页 > 解决方案 > 如何为类似状态单子的事物定义平面图

问题描述

我正在尝试解决如何flatmap为我的状态单子定义的东西。

class State(Protocol[T_co]):
    @abstractmethod
    def __call__(self: S, i: int) -> tuple[T_co, S]:
        ...

AState封装了状态。这里是unit

def unit(t: T) -> State[T]:
    return lambda _: (t, unit(t))

我从他们使用的一本书中得到了设计,State[S, A] = S -> (A, S)但我发现这有点麻烦(并且还需要额外的参数i),所以这里 aclass Foo(State[int])几乎就像 a State[Foo, int]。我想知道我简化它的尝试是否已经阻止它成为一个单子。

我尝试了类似的东西

def flatmap(f: Callable[[T], State[U]], s: State[T]) -> State[U]:
    def stateful(i: int) -> tuple[U, State[U]]:
        t, s_new = s(i)
        u, su = f(t)(i)
        return u, flatmap(f, s_new)  # or u, su

    return stateful

但尽管我认为这满足身份(尚未检查关联性),但我希望同时使用新状态s_newsu.

为了完整起见,这里有一个恐怖,它是flatmap从用于我的案例的书中的字面翻译S -> (A, S),带有一个额外的论点i: int。我对它会起作用的期望为零

def flatmap(f: Callable[[T], State[U]], s: State[T]) -> State[U]:
    class _State(State[U]):
        def __call__(self, i: int) -> tuple[U, _State]:
            x, y = type(s).__call__(self, i)
            return type(f(x)).__call__(y, i)

    return _State()

标签: pythonmonadsstate-monad

解决方案


我认为这是不可能的。状态的标准定义使用S -> (A, S). 添加一个额外的参数(S, int) -> (A, S)可能会或可能不会破坏它,但可能会是这样一个事实,即 a(S, int) -> (A, S)可以接受任何东西作为它的第一个参数,而 (a subclass of)class State的实例只能接受该类的实例作为self. 也就是说,我将自己限制在一个State只能接受自己作为第一个论点的范围内。这究竟是如何排除定义flatmap我仍在努力解决的。


推荐阅读