我想这个问题的总体答案会将我推向函数响应式编程,但是……请耐心等待。
我也没有这个问题的示例代码。我用我的一些代码在这个主题附近徘徊,但我一直牢牢地呆在 IO monad 中。
想象一下,我有一个应用程序,我在其中建模了一些复杂的状态并将其放入一个整体的应用程序状态单子中。我这样做是因为我希望我的核心应用程序和特定用户界面之间有一定程度的分离。
data S = S DataStore EventStream Sockets
type AppState m = StateT S m
(假设 DataStore、EventStream 和 Sockets 都是基本上按照它们听起来的样子的数据类型 :))
现在,假设我想在 GTK(TreeView,但没有子节点)中创建一个只查看 EventStream 的表。我已经学会了这样做listStoreNew event_stream >>= treeViewNewWithModel(参见http://markus.alyra.org/?p=1023,我在其中广泛讨论了设置的机制)。
但是,现在我的 AppState monad 中有一个可变的数据副本。当应用程序关闭并执行将新数据附加到 EventStream 的操作时,这些数据不会显示在视图中。我能想到让它显示在视图中的唯一方法是发送一条消息listStoreInsert my_new_event,除了对 monad 所做的更改之外。这是可行的,但开始感到笨拙。
更糟糕的是,这个神秘的树视图是一个管理视图!它是可编辑的!管理员说“哦,该事件有一些无效数据,我想更改它!”。现在,我可以毫无问题地更改上面创建的 ListStore 中的数据。我可以创建使更新毫无问题的回调。但我根本想不出如何将更新放入 Global AppState Monad。
最后几句话显示了问题的核心。如果我有一个全局 AppState Monad,那么更新该 monad 的任何内容都必须与想要查看该 monad 的所有内容一起执行。TreeView 打破了这一点。当在 TreeView monad 中编辑单元格时,编辑处理程序完全在 IO monad 中运行,并且预计不会返回任何内容。结束数据类型是IO (). 即使我有一些巧妙的方法从我的 AppState 中解包数据,然后执行编辑处理程序,然后在我的 AppState 中重新包装数据,应用程序的其他分支也看不到它。
即使我可以弄清楚如何创建自己的完全自定义的 ModelView 实例,该实例为我的 AppState 提供只读视图,我也无法想到如何使状态更新对应用程序的其余部分可用。
所以...
甚至可以以这种方式对 GTK/Haskell 应用程序进行建模吗?或者,我是否走上了疯狂的道路?