f#从函数返回部分函数

时间:2012-06-19 18:05:44

标签: f#

虽然将部分函数传递给另一个函数是微不足道的,但如何从函数中返回不同签名的部分函数?

以下是我尝试的基本代码,然后尝试各种方法:

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
    

    这会编译,但unbox尝试会抛出运行时错误:

      

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

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

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

  5. 尝试以各种方式向上转换部分功能。

2 个答案:

答案 0 :(得分: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!

答案 1 :(得分: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

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