Home > Enterprise >  How to Define Something that Acts as a Dictionary of Unique Functions in F#?
How to Define Something that Acts as a Dictionary of Unique Functions in F#?

Time:06-02

It is my understanding that a Dictionary's elements must all be the same (ie: unit -> unit)

I need a way to add functions to an object, similar to a Dictionary, but my problem is that the functions have different Types. The only thing I can know for sure is that added functions will always follow a pattern of int -> 'T where 'T could be any Type. All functions in the object could be made to inherit from a shared type if needed. Here is a non-functioning example of how it would be used.

let myFunctions = Dictionary<int,int -> unit>()

let exampleFunction0 (x : int) : int = x   1
let exampleFunction1 (x : int) : byte = (byte)x
let exampleFunction2 (x : int) : string[] = Array.create<string> 1 "test"

myFunctions.Add(0, exampleFunction0)
myFunctions.Add(1, exampleFunction1)
myFunctions.Add(2, exampleFunction2)

let randomNumber = System.Random().Next(3)

let result x = myFunctions.Item(randomNumber) x

It is important to note that which one of the functions that gets called is random and I cannot know it. The purpose of the dictionary is to hold the functions that will be called randomly. As in the examples, the results are not the same as I need the results to be different for each function. x will always be the same, but the result of the function will not.

The code will be in a library to be reused, so functions may be added that I will never see.

CodePudding user response:

You need to unify the output types to make this work.

If all the types are known you should use a Discriminated Union.

type ReturnValues = 
| Case0 of int
| Case1 of byte
| Case2 of string[]

let myFunctions = Dictionary<int,int -> ReturnValues>()

let exampleFunction0 (x : int) = x   1 |> Case0
let exampleFunction1 (x : int) = (byte)x |> Case1
let exampleFunction2 (x : int) = Array.create<string> 1 "test" |> Case2

myFunctions.Add(0, exampleFunction0)
myFunctions.Add(1, exampleFunction1)
myFunctions.Add(2, exampleFunction2)

let randomNumber = System.Random().Next(3)

let result x : ReturnValues = myFunctions.Item(randomNumber) x

If you don't know all the types you can make all the return types obj (Dictionary<int,int -> obj>) using the box function or you can make all the return types implement a common interface.

CodePudding user response:

What has ultimately solved the problem for me is to package together the Type and Object. This package is what the functions that will be added to the dictionary must return. This way there is a standard way for new types to be added after compilation.

open System.Collections.Generic
open System

// a package that can hold an object and its type
type package(x, y) =
    member this.x : Type = x
    member this.y : Object = y

//empty class for testing
type testClass() = class end

//custom types for testing
type fiy(x) = 
    member this.Value : string = x
type foe(x) = 
    member this.Value : testClass = x

//functions for testing
let func0 (x: int) : package = package(typeof<int>, x)
let func1 (x: int) : package = package(typeof<fiy>, fiy("potato"))
let func2 (x: int) : package = package(typeof<foe>, foe(testClass()))

let myFunctions = Dictionary<int, (int -> package)>()

//example use of adding a function
myFunctions.Add(0, (func0))
myFunctions.Add(1, (func1))
myFunctions.Add(2, (func2))

//pick a random number to test with; as we will have no idea which function will be ran
let randomNumber = System.Random().Next(3)
let pickedFunction x : package = (myFunctions.Item(randomNumber) x)

let ranFunction = pickedFunction 5 //just some filler number to test with

printfn "The type of the ranFunction is: %s" (ranFunction.x.ToString())
  • Related