3

虽然将部分函数传递给另一个函数很简单,但如何从函数返回不同签名的部分函数?

这是我正在尝试的基本代码,然后是各种尝试使其工作:

type InitData() =
    static member arrayIntAsc count = [|1..count|] 
    static member seqIntAsc count = {1..count}
    static member listIntAsc count = [1..count]
    (*more diverse signatures*)

module x =
    let getInitDataFun (initData:string) =
        match initData.ToLower() with
        | "arrayintasc" -> InitData.arrayIntAsc
        | "seqintasc" -> InitData.seqIntAsc
        | "listintasc" -> InitData.listIntAsc
        (*more diverse signatures*)
        | _ -> failwithf "InitData function %s not recognized" initData
  1. 尝试以各种方式强制使用通用返回签名,但 F# 3.0 总是将 getInitDataFun 返回签名强制为第一个匹配的签名:

    let getInitDataFun (initData:string) : 'a -> 'b  = ...
    let getInitDataFun (initData:string) : _ -> _  = ...
    let getInitDataFun (initData:string) : int -> #(int seq)  = ...
    let getInitDataFun (initData:string) : int -> #('a seq)  = ...
    (*even if I could get (int -> #(int seq)) to work, I would like to return
      signatures not in this pattern too*)
    
  2. 试过的盒子/拆箱:

    | "arrayintasc" -> box InitData.arrayIntAsc
    

    这会编译,但拆箱尝试会引发运行时错误:

    未处理的异常:System.InvalidCastException:无法将类型的对象'RangeInt32@4819-2'转换为类型'System.Collections.Generic.IEnumerable`1[System.Object]'

  3. 尝试将部分函数作为引号返回,但有类似的问题。如果我返回键入的引号,则返回不同的 Expr 签名时会遇到同样的问题。我可以返回无类型的引用,但是我必须在调用方知道返回的无类型表达式的签名。

  4. 考虑反射,但基本上是相同的问题,需要在调用时知道实际签名。

  5. 也尝试过以各种方式向上转换部分函数。

4

2 回答 2

2

您最好的选择是将静态成员更改为全部返回相同的类型:

type InitData() =
    static member arrayIntAsc count = seq [|1..count|] 
    static member seqIntAsc count = {1..count}
    static member listIntAsc count = seq [1..count]

或用执行强制转换的函数包装它们:

let getInitDataFun (initData:string) =
    let asSeq f x = f x :> seq<_>
    match initData.ToLower() with
    | "arrayintasc" -> asSeq InitData.arrayIntAsc
    | "seqintasc" -> InitData.seqIntAsc
    | "listintasc" -> asSeq InitData.listIntAsc

可以让它通用:

let getInitDataFun<'T when 'T :> seq<int>> (initData:string) : (int -> 'T) =
    match initData.ToLower() with
    | "arrayintasc" -> (box >> unbox) InitData.arrayIntAsc
    | "seqintasc" -> (box >> unbox) InitData.seqIntAsc
    | "listintasc" -> (box >> unbox) InitData.listIntAsc

但如果预期返回类型错误,它会产生运行时异常:

let f = getInitDataFun "arrayintasc"
let x : int list = f 10 //BOOM!
于 2012-06-19T18:29:44.627 回答
1

我认为要么这就是你想要的

type InitData() = 
    static member arrayIntAsc count = [|1..count|]  
    static member seqIntAsc count = {1..count} 
    static member listIntAsc count = [1..count] 
    (*more diverse signatures*) 

let getInitDataFun (initData:string) : obj = 
    match initData.ToLower() with 
    | "arrayintasc" -> box InitData.arrayIntAsc 
    | "seqintasc" -> box InitData.seqIntAsc 
    | "listintasc" -> box InitData.listIntAsc 
    (*more diverse signatures*) 
    | _ -> failwithf "InitData function %s not recognized" initData 

let a = ((getInitDataFun "arrayintasc") :?> int->int[]) 20
printfn "%A" a

或者我不清楚你在问什么。

于 2012-06-19T18:26:59.637 回答