我有一个看起来类似以下内容的用例:
type Event =
| EventA
| EventB
type IState =
abstract member HandleEvent: Event -> IState
type ExampleState =
{ ListOfSomething: int list }
interface IState with
member this.HandleEvent event =
match event with
| EventA -> this
| EventB -> this
let functionThatUsesIState (state: IState) (listOfStates: list<IState>) =
state :: listOfStates
使用接口的原因是因为我需要一个包含所有状态的集合,因此,我需要单一类型来使集合同构。在此示例中,为简单起见,我显示了 list<IState>
。在我的实际应用中,它是一个
Map<string, IState>
。
functionThatUsesIState
的用户创建一个类型,然后实现接口。这是okay-ish,但我更喜欢一种更实用的方法,这样
functionThatUsesIState
(或包装它的函数)不采用接口,而是只采用
'T
类型的值,然后是处理
'T
.的函数 例如,我可以定义:
,而不是在高层使用接口
type HowIdLikeToDefineState =
{ ListOfSomething: int list }
let howIdLikeToDefineHandleEvent (state: HowIdLikeToDefineState) (event: Event) =
match event with
| EventA -> state
| EventB -> state
如果我这样做的话,这可能会起作用:
let functionThatUsesState (state: 'T) (listOfStates: list<'T>) =
state :: listOfStates
这“有效”,但对我的应用程序没有好处。我真正需要的是 listOfStates
,其中
'T
元素之间并不总是(几乎从不)相同。
howIdLikeToDefineHandleEvent
在
IState
上动态实现
HowIdLikeToDefineState
,那就太好了。因为这样我就可以做类似的事情:
let highLevelFunctionThatUsesState (state: 'T) (eventHandler: 'T -> Event -> 'T) (listOfStates: list<IState>) =
// "dynamically" implement IState on 'T using eventHandler as the implementation of HandleEvent
let dynamicIStateBasedOnT = ...
// pass this to functionThatUsesIState
functionThatUsesIState dynamicIStateBasedOnT listOfStates
这似乎在F#中可能是可能的,因为类型扩展、对象表达式和反射,但我还没有能够编造出一些东西。我最大的努力是:
let DynamicState =
{ new IState with
member this.HandleEvent event = howIdLikeToDefineHandleEvent this event }
这显然行不通,但传达了这个想法。我不知道如何动态地将接口实现“附加”到现有类型。我什至不知道这是否可能。我也尝试过:
let DynamicState =
{ new HowIdLikeToDefineState with
interface IState with
member this.HandleEvent event = howIdLikeToDefineHandleEvent this event }
这也不起作用,因为new
之后的类型必须是对象表达式的接口。
Map<string, State<'T>>
类型,其中
'T
可以变化。显然这是不可能的,那么我还能如何在 F# 中解决此类问题呢?就我而言,除了一两个与
State<'T>
中的
'T
成员类似的成员之外,
HandleEvent
的成员实际上独立于
IState
。
the解决方案。
无论哪种方式,这里至少有一个解决方案:
type Event =
| EventA
| EventB
type IState =
abstract member HandleEvent: Event -> IState
type ExampleState =
{ ListOfSomething: int list }
interface IState with
member this.HandleEvent event =
match event with
| EventA -> this
| EventB -> this
let functionThatUsesIState (state: IState) (listOfStates: list<IState>) =
state :: listOfStates
type HowIdLikeToDefineState =
{ ListOfSomething: int list }
let howIdLikeToDefineHandleEvent (state: HowIdLikeToDefineState) (event: Event) =
match event with
| EventA -> state
| EventB -> state
///////////////////////////////////////////////////////////////
// Here's where the solution to the question actually starts //
// with the full program. //
///////////////////////////////////////////////////////////////
type GenericState<'State> =
{ Value: 'State
HandleEvent: 'State -> Event -> 'State }
interface IState with
member this.HandleEvent event = { this with Value = this.HandleEvent this.Value event }
let createGenericState (initialState: 'State) (eventHandler: 'State -> Event -> 'State) : IState =
{ Value = initialState; HandleEvent = eventHandler }
let highLevelFunctionThatUsesState (initialState: 'T) (eventHandler: 'T -> Event -> 'T) (listOfStates: list<IState>) : list<IState> =
let genericState = createGenericState initialState eventHandler
functionThatUsesIState genericState listOfStates