假设我有一个不可变的包装器:
template<class T>
struct immut {
T const& get() const {return *state;}
immut modify( std::function<T(T)> f ) const { return immut{f(*state)}; }
immut(T in):state(std::make_shared<T>(std::move(in))){}
private:
std::shared_ptr<T const> state;
};
如果我有 immut<Bob> b
,我可以将 Bob(Bob)
操作变成可以替换我的 b
的操作 .
template<class T>
std::function<immut<T>(immut<T>)> on_immut( std::function<void(T&)> f ){
return [=](auto&&in){ return in.modify( [&](auto t){ f(t); return t; } ); };
}
因此,如果 Bob
是 int x,y;
,我可以将天真可变代码 [](auto& b){ b.x++; }
变成 immut<Bob>
更新程序 .
现在,如果 Bob
反过来拥有 immut<Alice>
成员,那么会有 immut<Charlie>
成员 .
假设我有一个 Charlie
更新程序, void(Charlie&)
. 而且我知道 Charlie
我要更新的地方是 . 在可变的土地上,它看起来像:
void update( Bob& b ){
modify_charlie(b.a.c[77]);
}
我可能会把它分成:
template<class S, class M>
using get=std::function<M&(S&)>;
template<class X>
using update=std::function<void(X&)>;
template<class X>
using produce=std::function<X&()>;
void update( produce<Bob> b, get<Bob, Alice> a, get<Alice, Charlie> c, update<Charlie> u ){
u(c(a(b())));
}
甚至去代数并有(伪代码):
get<A,C> operator|(get<A,B>,get<B,C>);
update<A> operator|(get<A,B>,update<B>);
produce<B> operator|(produce<A>,get<A,B>);
void operator|(produce<A>, update<A>);
让我们根据需要将操作链接在一起 .
void update( produce<Bob> b, get<Bob, Alice> a, get<Alice, Charlie> c, update<Charlie> u ){std::
u(c(a(b())));
}
变
b|a|c|u;
步骤可以在任何地方拼接在一起 .
与immuts相同的是什么,它有一个名字吗?理想情况下,我希望这些步骤在可变的情况下与孤立但可组合一样,天真的“叶子代码”在可变状态结构上 .
1 回答
IDK是否存在子状态操作的通用名称 . 但是在Haskell中,有一个Lens可以提供你想要的 .
在C中,您可以将
lens
视为一对getter和setter函数,这两个函数都只关注其直接子部分 . 然后有一个作曲家将两个镜头组合在一起,聚焦在一个结构的更深的子部分上 .你可以写一个像这样的基本镜头:
请注意,此过程可以通过宏自动执行 .
然后,如果
Bob
包含immut<Alice>
,Alice
包含immut<Charlie>
,则可以写入2个镜头(Bob
到Alice
和Alice
到Charlie
),Bob
到Charlie
镜头可以由以下组成:Live Demo
NOTE:
Here是一个更完整的示例,线性内存增长w.r.t深度,没有类型擦除开销(
std::function
),并且具有完整的编译时类型检查 . 它还使用宏来生成基本镜头 .