我有2个模块:秒表和时钟,秒表发出开始和停止动作,时钟不发出任何动作。
秒表处理启动,停止和更新操作
单击手柄仅更新操作,但更新操作与秒表不同。
秒表行动类型
type ActionType =
| Start of StartActionData
| Stop of int
| Update of int
时钟动作类型
type ActionType =
| Update of (int * TimeZone)
我的应用程序使用秒表和时钟,因此应用程序操作tpe为:
type Action =
| StopWatch of StopWatchAction
| Clock of ClockAction
当一个模块(秒表或时钟)创建一个动作时,它会将该动作包装到一个包装函数中,该函数将返回一个应用程序可以使用的联合类型。
例如秒表会创建停止操作:
(wrapper:(StopWatchAction -> 'a)) {
``type`` = (Stop startTime)
index = index
}
to 'a
是因为模块不知道将使用它们的应用程序,但'a
是Application.Action联合类型。
当一个动作到达应用程序处理程序时,它将使用该联合类型将动作委托给模块处理程序:
match action with
| ApplicationTypes.StopWatch action ->
{state with//return application state
//set the stopwatch state with updated state
// provided by the mediator in stop watch
StopWatch =
StopWatchMediator.handleAction
state.StopWatch
action
}
问题是我有一个定时器模块,它采用(StopWatch.Action->Application.Action)
和(Clock.Action->Application.Action)
等包装器
它会每秒发出一次更新动作,更新动作还需要一个秒表和时钟的createAction函数,但这是为了以后的关注。
我遇到的问题是我无法创建('a->'b)
函数列表,计时器中的代码是:
let mutable wrappers:(('a -> 'b) list) = []
let addWrapper wrapper =
wrappers <- wrapper::wrappers
在我的主应用程序文件中,我尝试调用它:
addWrapper ApplicationTypes.StopWatch
addWrapper ApplicationTypes.Clock
计时器模块应该能够使用('a->'b)
的包装器定期发送任何类型的动作。
包装器和操作创建器都由应用程序注入,因为应用程序知道哪些模块需要一个计时器,如何包装操作以及用于创建操作的函数。)
答案 0 :(得分:4)
您正在点击F#“限制值”错误。如果您不熟悉它,请在继续本答复的其余部分之前阅读https://blogs.msdn.microsoft.com/mulambda/2010/05/01/finer-points-of-f-value-restriction/。我会试着解释一下。
在F#中,函数可以是通用的,但值必须是特定的。列表实际上不能包含两个不同的值;也就是说,您不能拥有'a -> 'b
个函数的列表,其中'a
表示列表中不同项目的不同内容。您只能拥有int -> string
个函数列表或Clock -> ClockResult
函数列表,依此类推。如果你想要一个函数列表,其中一些函数的类型为StopWatch.Action -> Application.Action
而其他函数的类型为Clock.Action -> Application.Action
,那么你需要以某种方式组合这些函数,以便它们在将它们存储在列表中之前是相同的类型。如,
type ActionWrapper =
| StopWatch of StopWatch.Action -> Application.Action
| Clock of Clock.Action -> Application.Action
let mutable wrappers : ActionWrapper list = []
let addWrapper (wrapper : ActionWrapper) =
wrappers <- wrapper :: wrappers
let addStopWatchWrapper (wrapper : StopWatch.Action -> Application.Action) =
addWrapper (StopWatch wrapper)
let addClockWrapper (wrapper : Clock.Action -> Application.Action) =
addWrapper (Clock wrapper)
现在,您的wrappers
列表属于同一类型,您可以根据需要使用addStopWatchWrapper
或addClockWrapper
功能。
P.S。为什么使用充当全局的mutable
包装器列表?对我来说,这有点代码味道:你会得到“远距离的怪异行动”,以及与变异全球状态相关的所有其他问题。您将在代码的不同部分之间存在依赖关系,具体取决于哪个部分首先运行(并改变全局状态)以及哪个运行第二个(并且看到变异的全局状态),但这些依赖关系将隐藏:你只是通过阅读代码就不会知道他们在那里。将该状态作为函数参数传递会更好;这样你的代码依赖是明确的,你可以在以后减少意外。 (在编程中,“惊喜”=“错误”)。但是将你的设计修改为不具有可变状态是一个更大的主题,所以我将自己限制在这个答案中来解释你的wrappers
列表发生了什么。