Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

f# return a partial function from a function

Tags:

f#

While it is trivial to pass a partial function into another function, how can I return partial functions of varying signatures from a function?

Here is the basic code I am attempting, followed by various attempts to get it to work:

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. tried forcing a generic return signature in various ways, but F# 3.0 always forced the getInitDataFun return signature to the signature of the very first match:

    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. Tried box/unbox:

    | "arrayintasc" -> box InitData.arrayIntAsc
    

    this compiles, but the unbox attempt throws a runtime error:

    Unhandled Exception: System.InvalidCastException: Unable to cast object of type 'RangeInt32@4819-2' to type 'System.Collections.Generic.IEnumerable`1[System.Object]'

  3. Tried returning the partial functions as quotations, but had similar problems. If I returned typed quotations, had the same problem with returning different Expr signatures. I could returned untyped quotations, but then I have to know on the calling side the signature of the returned untyped expression.

  4. Considered reflection, but basically the same issue with a need to know the actual signature when it comes time to invoke.

  5. Tried upcasting the partial functions in various ways too.

like image 995
Jack Fox Avatar asked Dec 27 '25 22:12

Jack Fox


1 Answers

Your best bet is to change the static members to all return the same type:

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

or wrap them with a function that does the cast:

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

You could make it generic:

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

but it would generate a runtime exception if the wrong return type was expected:

let f = getInitDataFun "arrayintasc"
let x : int list = f 10 //BOOM!
like image 171
Daniel Avatar answered Dec 30 '25 23:12

Daniel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!