简体   繁体   中英

How to define an array of functions of different signature in F#

I would like to be able to use an array of functions along this line:

type SomeFunc =
| StringFunc  of (int -> string)
| DecimalFunc of (int -> decimal)

let dataCols = Dict<string, SomeFunc>()
dataCols["A"] <- fun x -> sprintf "%d" x
dataCols["B"] <- fun x -> decimal x

// would want to have:
(dataCols["A"] 0) // "0"
(dataCols["B"] 0) // 0M

How could I express this idea in valid F# code?

The first thing is that you'll need to wrap the functions in StringFunc or DecimalFunc when putting them into the dictionary:

let dataCols = Dictionary<string, SomeFunc>()
dataCols["A"] <- StringFunc(fun x -> sprintf "%d" x)
dataCols["B"] <- DecimalFunc(fun x -> decimal x)

When you want to call the function, you'll need to handle the two cases separately. One issue here is that your two functions return different results. You cannot really do this directly, so when calling the function you'll have to return a boxed obj , or you'd have to define a new discriminated union that's either string or decimal. The first option looks like this:

let call func arg = 
  match func with
  | StringFunc f -> box (f arg)
  | DecimalFunc f -> box (f arg)

Then you can use the call helper:

call (dataCols["A"]) 0
call (dataCols["B"]) 0

The boxing means that you'll get back obj , but it's hard to say what would be the best way to handle this without knowing more about your specific situation.

From your code, I get the impression that the input type is always going to be the same (int in the example), in order to be able to call any column without knowing its type.

If so, you might want to use a DU for the return type, and not for the function type. This way you'll get the calling behaviour you want.

type ColumnValue =
| StringValue of string
| DecimalValue of decimal

let dataCols = Dictionary<string, (int -> ColumnValue)>()
dataCols.["A"] <- fun x -> StringValue (sprintf "%d" x)
dataCols.["B"] <- fun x -> DecimalValue (decimal x)

// what you get
dataCols.["A"] 0 // (StringValue "0")

match (dataCols.["A"] 0) with
| StringValue s -> printf "%s" s
| DecimalValue d -> printf "%M" d

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM