我正在尝试学习F#并整理了以下代码:
open System.Collections.Generic
type Version = Version of int
type AggregateId = AggregateId of int
type IEventdata = interface end
type EventMeta = EventMeta of AggregateId * Version
type Event = Event of EventMeta * IEventdata
type ICommandData = interface end
type CommandMeta = CommandMeta of AggregateId * Version
type Command = CommandMeta * ICommandData
type EventStore = {
GetEvents : AggregateId -> Event seq;
Insert : Event seq -> unit
}
let commandDispatcherFactory (mapping: (Command -> Event seq -> Event seq), es: EventStore) =
(
fun (command: Command) ->
match command with
| (CommandMeta(aggregateId, version), commandData) ->
let events = es.GetEvents (aggregateId)
let resultingEvents = mapping command events
(es.Insert(resultingEvents))
)
type DummyCommand =
| Command1
interface ICommandData
type DummyCommand2 =
| Command2
interface ICommandData
type UnsupportedCommand =
| Command3
let dummyMapping = (fun ((command, commandData):Command) (events: Event seq) ->
match commandData with
| :? DummyCommand as dummyCommand ->
(printfn "Handling Command: %A" dummyCommand)
Seq.empty<Event>
| :? DummyCommand2 as dummyCommand ->
(printfn "Handling Command: %A" dummyCommand)
Seq.empty<Event>
| _ ->
(printfn "Unsupported command: %A" commandData)
Seq.empty<Event>
)
let dummyEs = {
GetEvents = (fun (aggregateId) -> Seq.empty<Event>);
Insert = (fun (events:Event seq) -> ())
}
dummyMapping (CommandMeta(AggregateId(1),Version(1)), Command1) Seq.empty<Event>
dummyMapping (CommandMeta(AggregateId(1),Version(1)), Command2) Seq.empty<Event> // Why does this work?
let commandDispatcher = commandDispatcherFactory(dummyMapping, dummyEs)
commandDispatcher (CommandMeta(AggregateId(1),Version(1)), Command1)
commandDispatcher (CommandMeta(AggregateId(1),Version(1)), Command2) // this doesn't work
我知道它可能是一面文字,但我没有设法缩小范围。这是对commandDispatcher的最后一次调用无法正常工作,我无法弄清楚原因。我知道可能有其他事情要做得更好,但这是我正在努力的事情:)
答案 0 :(得分:3)
编译器根据初始用法专门化commandDispatcher
的类型。您可以使用强制转换来修复它:
commandDispatcher (CommandMeta(AggregateId(1),Version(1)), Command1 :> ICommandData)
commandDispatcher (CommandMeta(AggregateId(1),Version(1)), Command2) //works
答案 1 :(得分:3)
这是一个减少的重复:
let f x (y:System.IComparable) = ()
let g = f 1
g 3
g "test" // not okay
let g' : System.IComparable -> unit = f 1
g' 3
g' "test" // okay
let g'' x = f 1 x
g'' 3
g'' "test" // okay
编译器推断出g
的通用类型(在我的示例中为g:('a -> unit) when 'a :> System.IComparable
)。但是,g
是一个语法值,因此不能通用(请参阅有关值限制的问题),因此编译器需要选择特定类型。如果您根本不使用g
,则会出现编译错误。如果您只使用一种具体类型,编译器将选择该类型。如果您在多种类型下使用它,编译器将根据使用的第一种类型进行专门化,然后抛出错误。
两种解决方法是使用非泛型类型进行注释或进行eta-expand,以使g
不是语法值。