无法定义'a->'b函数列表

时间:2017-09-04 06:37:05

标签: generics module f#

我有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)的包装器定期发送任何类型的动作。

包装器和操作创建器都由应用程序注入,因为应用程序知道哪些模块需要一个计时器,如何包装操作以及用于创建操作的函数。)

1 个答案:

答案 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列表属于同一类型,您可以根据需要使用addStopWatchWrapperaddClockWrapper功能。

P.S。为什么使用充当全局的mutable包装器列表?对我来说,这有点代码味道:你会得到“远距离的怪异行动”,以及与变异全球状态相关的所有其他问题。您将在代码的不同部分之间存在依赖关系,具体取决于哪个部分首先运行(并改变全局状态)以及哪个运行第二个(并且看到变异的全局状态),但这些依赖关系将隐藏:你只是通过阅读代码就不会知道他们在那里。将该状态作为函数参数传递会更好;这样你的代码依赖是明确的,你可以在以后减少意外。 (在编程中,“惊喜”=“错误”)。但是将你的设计修改为不具有可变状态是一个更大的主题,所以我将自己限制在这个答案中来解释你的wrappers列表发生了什么。