为什么我的例子中不支持这个泛型函数?

时间:2013-12-13 21:54:45

标签: f#

我正在尝试学习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的最后一次调用无法正常工作,我无法弄清楚原因。我知道可能有其他事情要做得更好,但这是我正在努力的事情:)

2 个答案:

答案 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不是语法值。