First of all, there are some issues with your code. You have added a type annotation to your dictionary of 'T -> 'T -> 'T
but it seems to me that it should return a function of type 'T[] -> 'T
since you are trying to give it an array and evaluate a reduction.
Also, [| 1, 2 |]
is an array of one tuple, you need an array of multiple values, such as this one : [| 1; 2 |]
I fixed your code like this:
let reduce values =
Array.reduce values
let primitivesEnv =
let r = System.Collections.Generic.Dictionary<string,'T[] ->'T>()
r.["int_+"] <- reduce ((+) : int -> int -> int)
r.["int_-"] <- reduce ((-) : int -> int -> int)
r
let result = primitivesEnv.["int_+"] [| 1; 2 |]
Unfortunately, this isn't the end of the problems.
We might have said that the dictionary is of type 'T[] ->'T
but it isn't, the only valid type for the dictionary is int[] -> int
, the first int -> int -> int
type annotation creates that constraint. If we leave out that type annotation, 'T
is constrained to int
when we use it with an int[]
.
The type parameter 'T
must always be resolved to a fixed type in some way, it isn't a wild card that lets you use anything.
This means that the dictionary approach is fine until you want to add float
(or some other type) in addition to just int
. Your only option, when using a dictionary, is to either choose one type or throw away some of your type-safety and resolve the specific type at runtime.
The simplest change to do that would be to create some union cases to describe the different reduction types:
type Reduction =
|IntReduction of (int[] -> int)
|FloatReduction of (float[] -> float)
You then create a Dictionary<string, Reduction>
instead of a Dictionary<string,'T[] ->'T>
.
When it comes to the more general problem of creating an interpreter in F#, I would start by creating a structured set of discriminated unions to describe the expressions and structure of the mini-language you wish to interpret, this is called an Abstract Syntax Tree (AST).
You can then define a run
function that walks the tree and performs all the computations described by the AST.
I would also use a parser combinator library (I'd recommend FParsec) to parse whatever structured text into an abstract syntax tree of the union cases you defined in the step above.
Phillip Trelford has an example online of how to do this using FParsec for a simple C# AST: http://www.fssnip.net/lf This is probably far more powerful than what you need but hopefully it'll give you a starting point.