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
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*)
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]'
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.
Considered reflection, but basically the same issue with a need to know the actual signature when it comes time to invoke.
Tried upcasting the partial functions in various ways too.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With