为什么我的"code not sufficiently generic. The type variable 'a could not be generalized because it would



有办法让这个通用函数工作吗?

module MarketData
open System.Collections.Generic
//start of mocks
type Name = Name of string with static member GetName = Name
type Element = 
Element of string
with
member this.GetElementAsString x = ""
member this.HasElement x = true
member this.GetElement x = this
member this.GetValueAsElement x = this
member this.NumValues = 0
type MessageType = MessageType of string with member this.Equals s = this = MessageType s
type Msg = 
{
msg: string
MessageType: MessageType
}
member this.HasElement x = true
member this.GetElement x = Element ""
module Event =
type EventType =
|RESPONSE
|PARTIAL_RESPONSE
|SESSION_STATUS
type Event =
{
Messages: Msg seq
Type: Event.EventType
}
interface IEnumerable<Msg> with
member this.GetEnumerator () =
this.Messages.GetEnumerator()
type Session =
Session of string
with
member this.NextEvent() = {Messages = Seq.empty; Type = Event.EventType.RESPONSE}
// end of mocks

type FieldName = 
FieldName of Name
with 
member this.AsName = let (FieldName x) = this in x
static member FromString s = Name.GetName(s) |> FieldName
type SecurityName = SecurityName of string with member this.AsString = let (SecurityName x) = this in x
type ExtractState<'a> = 
{
Errors:string list
Data:'a list
}
member this.ConsError e = {this with Errors = e::this.Errors}
member this.ConsData x = {this with Data = x::this.Data}
static member Empty = {Errors =[]; Data = []}
type RecordConstructor<'a> = SecurityName -> Element -> 'a
type SecurityProcessor<'a> = ExtractState<'a> -> Element -> ExtractState<'a>
module BbdData =
[<RequireQualifiedAccess>]
module Names =
let SecurityData = Name.GetName("securityData")
let Security = Name.GetName("security")
let FieldData = Name.GetName("fieldData")
let ResponseError = Name.GetName("responseError")
let SecurityError = Name.GetName("securityError")
let FieldExceptions = Name.GetName("fieldExceptions")
let FieldId = Name.GetName("fieldId")
let ErrorInfo = Name.GetName("errorInfo")
let Category = Name.GetName("category")
let Message = Name.GetName("message")
let (|CompleteResponse|_|) (event:Event) =
if event.Type = Event.EventType.RESPONSE then
event |> Some
else
None
let (|PartialResponse|_|) (event:Event) =
if event.Type = Event.EventType.PARTIAL_RESPONSE then
event |> Some
else
None 
let (|SessionTerminated|_|) (event:Event) =
if event.Type = Event.EventType.SESSION_STATUS then
if event |> Seq.exists (fun m -> m.MessageType.Equals("SessionTerminated")) then
Some ()
else
None
else
None

//let sessionOptions = SessionOptions()
//let session = new Session(sessionOptions)
//session.Start() |> printfn "session open: %b" 
//session.OpenService("//blp/refdata") |> printfn "service is open: %b"
let session = Session ""
let eventErrorMsg errorPrefix (elemError:Element) =
let category = elemError.GetElementAsString(Names.Category)
let msg = elemError.GetElementAsString(Names.Message)
sprintf "%s -> %s -> %s" errorPrefix category msg
let processRefSecurity<'a> (f:RecordConstructor<'a>) (state:ExtractState<'a>) (elemSecurity:Element) =
let strSecurity = elemSecurity.GetElementAsString(Names.Security)
if elemSecurity.HasElement(Names.SecurityError) then
elemSecurity.GetElement(Names.SecurityError) |> eventErrorMsg strSecurity |> state.ConsError
else
elemSecurity.GetElement(Names.FieldData) |> f (SecurityName strSecurity) |> state.ConsData
let processSecurities<'a> (securityData:Element) (f:SecurityProcessor<'a>) n initialState =
let rec innerLoop iSec state =
if iSec > n - 1 then
state
else
securityData.GetValueAsElement(iSec) |> f state |> innerLoop (iSec - 1)
innerLoop 0 initialState            
let processResponseEvent<'a> (event:Event) f (state:ExtractState<'a>) =
event |> Seq.fold (fun (innerState:ExtractState<'a>) msg ->
if msg.HasElement(Names.ResponseError) then
msg.GetElement(Names.ResponseError) |> eventErrorMsg "Request Failed" |> innerState.ConsError
else
let securityData = msg.GetElement(Names.SecurityData)
let n = securityData.NumValues          
processSecurities securityData f n innerState
) state
let eventLoop f outerState =
let rec innerLoop state =
match session.NextEvent() with
|PartialResponse event ->
let newState = processResponseEvent event f state
innerLoop newState
|CompleteResponse event ->
let newState = processResponseEvent event f state
newState
|SessionTerminated() ->
failwith "session terminated"
|_ -> innerLoop state
innerLoop outerState        
let sendRefRequest<'a> securities fields (f:RecordConstructor<'a>) =
//let refDataService = session.GetService("//blp/refdata")
//let request = refDataService.CreateRequest("ReferenceDataRequest")
//let elemSecurities = request.GetElement("securities")
//securities |> Seq.iter(fun (s:string) -> elemSecurities.AppendValue(s))
//let elemFields = request.GetElement("fields")
//fields |> Seq.iter(fun (s:string) -> elemFields.AppendValue(s))
//session.SendRequest(request, null) |> ignore
let secProcessor : SecurityProcessor<'a> = processRefSecurity f
eventLoop secProcessor ExtractState<'a>.Empty

这应该是彭博社提供的API的便利包装。其想法是用户提供一个功能,该功能可以从彭博社提供的Element创建记录。元素的内容取决于请求的特定字段。CCD_ 2从CCD_ 3中提取所有这些字段值并生成CCD_。

根据编译器的说法,sendRefRequest不够通用。

在我看来,问题就在这里:

type ExtractState<'a> = 
{
Errors:string list
Data:'a list
}
member this.ConsError e = {this with Errors = e::this.Errors}
member this.ConsData x = {this with Data = x::this.Data}
static member Empty = {Errors =[]; Data = []}

最后一行没有将空列表Data约束为list<'a>

如果你在某个地方添加了一个类型注释来修复这个问题,错误消息就会消失:

static member Empty = {Errors =[]; Data = []} : ExtractState<'a>

BTW:这将带你进入下一个错误:

~vsFE66.fsx(36,19): error FS0366: No implementation was given for 'System.Collections.IEnumerable.GetEnumerator() : System.Collections.IEnumerator'. Note that all interface members must be implemented and listed under an appropriate 'interface' declaration, e.g. 'interface ... with member ...'.

只需添加:interface System.Collections.IEnumerable with member this.GetEnumerator () = this.Messages.GetEnumerator() :> System.Collections.IEnumerator

相关内容

最新更新