我目前正在用 F# 重新开发一个应用程序,虽然体验非常好,但在控制可变性方面我发现自己有点困惑。
以前,我的 C# 程序使用的文档模型是高度可变的,并且实现了 ObservableCollections 和 INotifyPropertyChanged,视图之间的共享状态不会出错。显然,这不是一个理想的选择,特别是如果我想要一种完全不可变的设计方法。
考虑到这一点,我为我的底层应用程序内核创建了一个不可观察、不可变的文档模型,但是,因为我希望 UI 订阅者看到更改,我立即发现自己实现了事件驱动模式:
// Raw data.
type KernelData = { DocumentContent : List<string> }
// Commands that act on the data.
type KernelCommands = { AddString : string -> () }
// A command implementation. Performs a state change, echos the new state through the event.
let addStringCommand (kernelState : KernelData) (kernelChanged : Event<KernelData>) (newString : string) =
kernelState with { DocumentContent=oldList |> List.add newString }
|> kernelChanged.Trigger
// Time to wire this up.
do
// Create some starting state.
let kernelData = { DocumentContent=List.Empty }
// Create a shared event that commands may use to inform observers (UI).
let kernelChangedEvent = new Event<KernelData>()
// Create the command, it uses the event to inform observers.
let kernelCommands = { AddString=addString kernelData kernelChangedEvent }
// Create a UI element that uses the commands to initialize data transformations. UI elements subscribed to the data use the event to listen.
let myUI = new UiObject(kernelData, kernelChangedEvent.Publish, kernelCommands)
myUI.Show()
所以这是我将新状态传递给相关听众的解决方案。然而,更理想的是我可以用转换函数“挂钩”的“盒子”。当盒子发生变化时,会调用函数来处理新状态并在 UI 组件中产生相应的变化状态。
do
// Lambda called whenever the box changes.
idealBox >>= (fun newModel -> new UIComponent(newModel))
所以我想我在问是否有一个可观察的模式来处理这些情况。可变状态通常使用 monad 处理,但我只看到涉及执行操作的示例(例如管道控制台 IO monad、加载文件等),而实际上并未处理持续变化的状态。